简体   繁体   中英

map<Key,pair<A,B>> to map<Key,A>

I have a class that for some reasons needs to store a pair (say int,double) for each key, but whose interface only makes public one of the two values of the pair (int). One implementation may be

using namespace std;
class Foo {
public:
  map<string,int> const & get() const {return external;}
  void doStuff(string const & str) {
    external.at(str);
    internal.at(str);
  }
private:
  map<string,int> external;
  map<string,double> internal;
};

The drawback is that doStuff has double lookup.

Another way is to store map<string,pair<int,double>> allin but then the accessor returning the whole map requires a copy.

The last way, of course, is to make the accessor more atomic by accessing a single key with a return allin.at(key).first; , which seems the best way but it breaks the interface I would like to provide.

I wonder, is there a way to avoid the double lookup in the "external/internal" version?

Another way could be to define a map entry with a private and a public value. Via a friend declaration the private value can be made visable to Foo class.

#include <iostream>
#include <string>
#include <map>

class Foo;
template<typename T1, typename T2>
struct my_map_entry
{
    public:
        my_map_entry()=default;
        my_map_entry(T1 v, T2 pv)
        {
            value = v;
            private_value = pv;
        }
        T1 value;
    private:
        T2 private_value;
        friend class Foo;
};

class Foo {
    public:
        Foo(){
            map["test"]=my_map_entry<int, double>(1, 2.0);
        }
    
        void doStuff(const std::string& str
        {
            auto entry = map.at(str);
            std::cout<<"private_value: "<<entry.private_value<<std::endl;
        }
    
        std::map<std::string,my_map_entry<int, double>> map;
};

int main()
{
    Foo test;
    test.doStuff("test");
}

Given you just want the user to be able to retrieve the item, your get() method can be rewritten bearing that requirement in mind.

using namespace std;
class Foo {
public:
  int *const get(string const &str) const {
    auto it = map_.find(str)
    return it == map_.end() ? nullptr : &it->first;
  }

  void doStuff(string const & str) {
    map_.at(str);
  }
private:
  map<string,pair<int, double>> map_;
};

The idea is to return nullptr if the key was not found, or a pointer to the value if the key was found.

If instead you also want to be able to enumerate all the available ints, then you only have three choices:

  1. Make your Foo class a map-like class, with its own iterators that wrap the ones from the inner map.
  2. Use one of the available libraries to provide a view that helps you achieving the previous goal (like boost::transform_iterator )
  3. As the other answer suggests, use a struct as the map's value and make one of its fields private, with possibly a conversion operator to int.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM