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?
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With