Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use comparator with is_transparent type?

Tags:

c++

c++14

stdset

with C++14 we are allowed to compare elements of some associative containers (like std::set) with other types than the ones stored in a container. It's supposed to work when comparator has is_transparent denoted as a type (see e.g. std::set::find).

Suppose I have a string wrapper which performs some checks on a string (if it's format is valid format and so on - not really important, but constructing it is heavy enough that I'd like to avoid it + it can throw exceptions) and it's stored in std::set to have a container of unique values. How should I write a comparator for it? Should it look like like the one below? Can I overload and use my sw::operator<() to achieve the same?

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;
}

I believe that above code should work as expected, but when I tested it with g++4.9 and clang++3.6, both yielded errors about missing conversion from string to key_type as if string overloads of Comparator::operator() were never taken into account. Am I missing something?

like image 810
Michał Góral Avatar asked Feb 01 '15 17:02

Michał Góral


1 Answers

Yes, that code is correct, but it would be simpler to overload operator< to allow comparing your type with std::string and then just use std::less<> (i.e. std::less<void>) which is "transparent" already.

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"} };

Also, it's possibly worth noting that it doesn't matter what you define is_transparent to, either of these would have the same effect as your definition of it:

using is_transparent = std::false_type;

using is_transparent = void;
like image 127
Jonathan Wakely Avatar answered Oct 20 '22 01:10

Jonathan Wakely