Как использовать компаратор с типом is_transparent?

с C++14 нам разрешено сравнивать элементы некоторых ассоциативных контейнеров (например, std::set) с другими типами, кроме тех, которые хранятся в контейнере. Это должно работать, когда компаратор имеет is_transparent, обозначенный как тип (см., например, std::set::find).

Предположим, у меня есть оболочка строки, которая выполняет некоторые проверки строки (если ее формат является допустимым форматом и т. д. - не очень важно, но ее создание достаточно тяжелое, и я хотел бы избежать этого + он может генерировать исключения), и это хранится в std::set, чтобы иметь контейнер уникальных значений. Как мне написать для него компаратор? Должен ли он выглядеть так, как показано ниже? Могу ли я перегрузить и использовать свой sw::operator<() для достижения того же?

class sw
{
public:
    explicit sw(const std::string& s) : s_(s) { /* dragons be here */ }
    const std::string& getString() const { return s_; }

    bool operator<(const sw& other) const { return s_ < other.s_; }

private:
    std::string s_;
};

struct Comparator
{
    using is_transparent = std::true_type;

    bool operator()(const sw& lhs, const std::string& rhs) const { return lhs.getString() < rhs; }
    bool operator()(const std::string& lhs, const sw& rhs) const { return lhs < rhs.getString(); }
    bool operator()(const sw& lhs, const sw& rhs) const { return lhs < rhs; }
};

int main() 
{
    std::set<sw, Comparator> swSet{ sw{"A"}, sw{"B"}, sw{"C"} };
    std::cout << std::boolalpha << (swSet.find(std::string("A")) != swSet.end()) << std::endl;
}

Я считаю, что приведенный выше код должен работать, как и ожидалось, но когда я тестировал его с помощью g++ 4.9 и clang++ 3.6, оба выдавали ошибки об отсутствии преобразования из string в key_type, как будто перегрузки строк Comparator::operator() никогда не учитывались. Я что-то упускаю?


person Michał Góral    schedule 01.02.2015    source источник
comment
Версия libstdc++ (стандартная библиотека gcc), поставляемая с g++4.9, не реализует гетерогенный поиск. Ваш код прекрасно компилируется с clang и libc++.   -  person T.C.    schedule 01.02.2015
comment
@T.C.: спасибо, это имеет смысл, и именно этого я и боялся ... Я попробовал это с clang и -stdlib=libc++, и это сработало, как и предполагалось. Жаль, что моя среда по умолчанию — g++ и libstdc++.   -  person Michał Góral    schedule 01.02.2015
comment
Он реализован в gcc.gnu.org/viewcvs/gcc?view=revision&revision=219888. менее двух недель назад.   -  person T.C.    schedule 01.02.2015


Ответы (1)


Да, этот код правильный, но было бы проще перегрузить operator<, чтобы разрешить сравнение вашего типа с std::string, а затем просто использовать std::less<> (т.е. std::less<void>), который уже "прозрачный".

inline bool operator<(const sw& lhs, const std::string& rhs) { return lhs.getString() < rhs; }
inline bool operator<(const std::string& lhs, const sw& rhs) { return lhs < rhs.getString(); }

std::set<sw, std::less<>> swSet{ sw{"A"}, sw{"B"}, sw{"C"} };

Кроме того, возможно, стоит отметить, что не имеет значения, для чего вы определяете is_transparent, любой из них будет иметь тот же эффект, что и ваше определение:

using is_transparent = std::false_type;

using is_transparent = void;
person Jonathan Wakely    schedule 20.10.2016
comment
Хм, я не знал о less<void> и его поведении, это мило. Тем не менее, когда я делаю такие вещи, я нахожу отдельный компаратор более чистым и выразительным, но это, вероятно, личное предпочтение. :) - person Michał Góral; 24.10.2016