Использование std :: reference_wrapper в качестве ключа в std :: map - PullRequest
8 голосов
/ 04 февраля 2012

У меня есть куча объектов в иерархии классов, и я хотел бы создать std::map, используя ссылки на эти объекты в качестве ключей на карте. Кажется, что std::reference_wrapper было бы именно то, что нужно для этого, но я не могу заставить его работать. Что я пробовал до сих пор:

class Object { // base class of my hierarchy
    // most details unimportant
public
    virtual bool operator< (const Object &) const;  // comparison operator
};

std::map<std::reference_wrapper<const Object>, int> table;

auto it = table.find(object);

table[object] = 42;

table[object]++

Однако я всегда получаю несколько неясных ошибок от компилятора:

/usr/include/c++/4.5.3/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = std::reference_wrapper<const Object>]’:
/usr/include/c++/4.5.3/bits/stl_tree.h:1522:38:   instantiated from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::find(const _Key&) [with _Key = std::reference_wrapper<const Object>, _Val = std::pair<const std::reference_wrapper<const Object>, int>, _KeyOfValue = std::_Select1st<std::pair<const std::reference_wrapper<const Object>, int> >, _Compare = std::less<std::reference_wrapper<const Object> >, _Alloc = std::allocator<std::pair<const std::reference_wrapper<const Object>, int> >, std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::pair<const std::reference_wrapper<const Object>, int> >]’
/usr/include/c++/4.5.3/bits/stl_map.h:697:29:   instantiated from ‘std::map<_Key, _Tp, _Compare, _Alloc>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const key_type&)[with _Key = std::reference_wrapper<const Object>, _Tp = int, _Compare = std::less<std::reference_wrapper<const Object> >, _Alloc = std::allocator<std::pair<const std::reference_wrapper<const Object>, int> >, std::map<_Key, _Tp, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::pair<const std::reference_wrapper<const Object>, int> >, key_type = std::reference_wrapper<const Object>]’
testfile.cpp:39:31:   instantiated from here
/include/c++/4.5.3/bits/stl_function.h:230:22: error: no match for ‘operator<’ in ‘__x < __y’

Ошибка, похоже, говорит о том, что она не может сравнивать два std::reference_wrapper<const Object> объекта, но кажется, что это возможно - std::reference_wrapper имеет оператор преобразования, который может неявно преобразовать его в T& (const Object & здесь), а Object имеет operator <, так почему же он не работает?

Должно ли это работать, и это просто ошибка в g ++? Или что-то еще происходит?

Ответы [ 3 ]

6 голосов
/ 12 октября 2017

По умолчанию std::map использует std::less<std::reference_wrapper<const Object>> как Compare, но std::reference_wrapper<T> не передает operator<() базовому типу T.

Простейшийи кратчайший путь решения вашей проблемы - определить std::less<const Object> (или std::greater<const Object>) в определении карты следующим образом:

std::map<std::reference_wrapper<const Object>, int, std::less<const Object>> table;

Он будет работать правильно и как ожидалось из-за неявного преобразования std::reference_wrapper до T& и неявный конструктор std::reference_wrapper.

Пример .

6 голосов
/ 04 февраля 2012

Кажется, что это сработало бы, если бы вы сделали оператор сравнения бесплатной функцией (которая, возможно, вызывает функцию виртуального члена).

Если это функция-член, a < b действительно означает a.operator<(b);, и неявные преобразования не учитываются для левого аргумента.

1 голос
/ 23 мая 2012

В Visual Studio 11 Beta возникает та же проблема. Использование бесплатной версии, которая вызывает оператор <, решает проблему. </p>

#include<map>
#include<iostream>
using namespace::std;

class Object { 
    int _n1;
public:

    Object(int n = 0):_n1(n){};
    bool operator < (const Object& rhs) const {return this->_n1 < rhs._n1;} 
    friend ostream &operator << (ostream &stream, const Object& o) { stream << o._n1 << " "; return stream;}
};

struct ObjectLess{

    bool operator()(const Object& lhs, const Object& rhs) const 
    {
        return lhs<rhs;
    }
};

int main(int argc, char* argv[])
{
    //This does not compile
    //std::map<std::reference_wrapper<const Object>, string> table;

    //Using the free function works
    std::map<std::reference_wrapper<const Object>, string, ObjectLess> table;

    Object a(1);
    Object b(2);
    Object c(3);


    table[a]="One";
    table[c]="Three";
    table[b]="Two";

    for(auto y: table){
    cout << y.first << " " << y.second.c_str() << std::endl;
}


    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...