3

I have a list of Objects that store some Data. Once an object is added to the list it will never be removed. Given some data, I would also like to quickly look up which object it came from.

Rather than loop over the entire list and check each object I would like to create a map from data to objects.

#include <vector>
#include <map>

class Data {/*Stuff here*/};

struct Object {
    Data d;
    Object(Data d) : d(d) {}
};

class ObjectStorage {
    std::vector<Object> objects;
    std::map<Data&, Object&> reverse_lookup;

public:
    void add_object(Data d) {
        objects.emplace_back(d);

        auto &obj = objects.back();
        reverse_lookup.emplace(obj.d, obj);
    }
};

int main() {
    ObjectStorage S;
    S.add_object(Data());
}

To save space I used references in the map. The objects and data are already stored in the list and I know they will never be removed.

However, I am getting the following error when trying to compile this program.

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/map:1024:18: error: multiple overloads of 'operator[]' instantiate to the same signature 'std::__1::map<Data &, Object &, std::__1::less<Data &>, std::__1::allocator<std::__1::pair<Data &, Object &> > >::mapped_type &(std::__1::map<Data &, Object &,
      std::__1::less<Data &>, std::__1::allocator<std::__1::pair<Data &, Object &> > >::key_type &)'
    mapped_type& operator[](key_type&& __k);
                 ^
test.cpp:13:27: note: in instantiation of template class 'std::__1::map<Data &, Object &, std::__1::less<Data &>, std::__1::allocator<std::__1::pair<Data &, Object &> > >' requested here
        std::map<Data&, Object&> reverse_lookup;
                                 ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/map:1022:18: note: previous declaration is here
    mapped_type& operator[](const key_type& __k);
                 ^
1 error generated.

The main portion of the error seems to be:

error: multiple overloads of 'operator[]' instantiate to the same signature

Increasingly Idiotic
  • 5,700
  • 5
  • 35
  • 73
  • 1
    looks like the original question is answered, but you might be able to improve your solution by either: A. Using std::unordered_map instead of std::map, lookup is O(1) instead of O(lg(N)) so in theory would be much faster.(You'd have to define a hash function though). OR B. Since you are adding "bool operator<(const Data& lhs, const Data& rhs)" consider storing your objects in sorted vector(sorted by Data). This will allow you to use binary_search when doing a lookup but avoid another copy of objects, thus saving space with little if any additional cost in performance – David Mar 18 '19 at 23:27
  • Slightly off topic, but google could only find this link for the error "multiple overloads of address instantiate to the same signature", for (my case) vectors: [Does C++ allow vector?](https://stackoverflow.com/q/6954906/) (No.) [Vector of const pointers?](https://stackoverflow.com/q/29378533/) (No.) – JWCS Feb 16 '21 at 00:08

1 Answers1

4

The error stems from using references in std::map. See the question "Why Can't I store references in a std::map in C++?" for more information as to why.

If it is not too expensive, changing the reverse_lookup map to store copies via

std::map<Data, Object> reverse_lookup;

and adding a definition for bool operator<(const Data& lhs, const Data& rhs) to allow for Data to be used as a key will solve the problem.

If copying Data and Object is too expensive a map of pointers can be used instead of references. However, be careful about how the pointed-to data is stored. In this case, if the objects vector grows past its internal capacity and is resized, all of the pointers will become invalid. Using something like std::list that does not move data around in memory should prevent that issue. Credit to Christophe for pointing this out in the comments.

Increasingly Idiotic
  • 5,700
  • 5
  • 35
  • 73
  • 1
    In addition, the objects in the vector may be moved in memory when the capacity of the vector has to be increased. So references might be dangling. – Christophe Mar 18 '19 at 22:21
  • @Christophe Good point! I believe that also means if pointers were used in the map they would all be invalid if the `objects` vector reaches its internal capacity and increases its internal size. – Increasingly Idiotic Mar 18 '19 at 22:34
  • 1
    yes, exactly ! The only way to do it is to map an id (but I think it’s the idea of data) to an index in the vector and tranform the index in a pointer or a reference only when the data is searched for (being understood that it might be invalid as soon as an object is added. – Christophe Mar 18 '19 at 22:41