Перегрузка оператора == для const std::reference_wrapper в std::unordered_map

Я не могу понять, как получить ссылку std::string в std::unordered_map с помощью std::reference_wrapper. По следующей ссылке я понимаю, что мне нужно перегрузить operator==.

Почему экземпляры шаблона не могут быть выведены в `std:: reference_wrapper?

Однако я не могу понять, как написать operator== так, чтобы он принимал const std::reference_wrapper. Если бы оболочка не была const, это не было бы проблемой.

Использование символа вместо std::string работает нормально (не требует перегрузки operator==).

Код:

#include <iostream>
#include <unordered_map>
#include <functional>

bool operator==(const std::reference_wrapper<std::string> lhs,
                const std::reference_wrapper<std::string> rhs)
{
    return std::equal_to<std::string>()(lhs.get(), rhs.get());
}

int main(){
    char        chr('a');
    std::string str("b");
    int         num(1);

    // this works (char)
    std::unordered_map<std::reference_wrapper<char>, int, std::hash<char>> charMap;
    std::pair<std::reference_wrapper<char>, int> charPair(chr , num);
    charMap.insert(charPair);
    std::cout << "charMap works.  Output: " << charMap[chr] << std::endl;

    // does not work (std::string)
    std::unordered_map<std::reference_wrapper<std::string>, int, std::hash<std::string>> stringMap;
    std::pair<std::reference_wrapper<std::string>, int> stringPair(str , num);
    stringMap.insert(stringPair);  // compile error
}

Ошибка компиляции:

error: no match for ‘operator==’ (operand types are ‘const std::reference_wrapper<std::__cxx11::basic_string<char> >’ and ‘const std::reference_wrapper<std::__cxx11::basic_string<char> >’)
       { return __x == __y; }

person dontarius    schedule 19.09.2015    source источник


Ответы (1)


Вы не можете предоставить собственную перегрузку для operator== для типов, не определяемых пользователем. То есть, в лучшем случае, неопределенное поведение. Однако здесь этого делать не нужно. std::unordered_map имеет пять параметров шаблона:

template<
    class Key,
    class T,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;

Видишь 4-й? Это то, что ты хочешь. Вам нужно предоставить функцию для сравнения. К счастью, вы можете использовать std::hash и std::equal_to следующим образом:

std::unordered_map<
    std::reference_wrapper<std::string>,
    int,
    std::hash<std::string>,
    std::equal_to<std::string>
> stringMap;
person Barry    schedule 19.09.2015
comment
Да, это правильное решение. К сожалению, std::reference_wrapper не указывает operator== (было бы легко просто перенаправить его в operator== базового типа), а также не предоставляет метод хеширования. Поэтому нужно всегда быть явным при создании unordered_map или unordered_set с ними. - person Kai Petzke; 04.09.2020