I want to construct a set with lambda comparator.
Due to known limitations you can not specify lambda as template parameter(you need to decltype()
it) so I thought about specifying the key of the map in the template argument list and comparator in the constructor argument.
Something like:
std::set<Color> colors({ { "Red", 255, 0 , 0 }, { "Green", 0,255,0 }, { "Black", 0,0,0 } } , [](const Color& a, const Color& b){return a.name()<b.name();});
But from what I understand from error message as soon as I specified the template arguments(<Color>
) I forced others to default(std::less
for comparator). And the map constructor taking just comparator is not smart enough to get the Key type from comparator arguments, aka this does not work:
std::set colors([](const Color& a, const Color& b){return a.name()<b.name();});
Is there a way to specify I want a set of Color
s, but let the comparator be specified by constructor.
Note that I can use constructor to deduce template types since C++17, but it is not pretty since I need to write a lot more than I want.
std::set colors(std::initializer_list<Color>{ { "Red", 255, 0 , 0 }, { "Green", 0,255,0 }, { "Black", 0,0,0 } } , [](const Color& a, const Color& b){return a.name()<b.name();}, std::allocator<char/*???*/>{});
full code here:
If I remember correctly, isn't possible with deduction guides (in C++17) explicit a template type and deduce the others.
If you want deduce the type Color
from the lambda comparator, the best I can imagine is the creation of a makeSetFromCmp()
function
template <typename Key>
auto makeSetFromCmp (bool(*cmp)(Key const &, Key const &),
std::initializer_list<Key> const & il)
{ return std::set(il, cmp); }
The trick is that passing first the comparator, the Key
type can be deduced from the comparator so there is non need of explicit std::initializer_list<Key>
calling the function.
So you can write
auto colors = makeSetFromCmp(+[](Color const & a, Color const & b)
{ return a.name() < b.name(); },
{ { "Red", 255, 0 , 0 },
{ "Green", 0,255,0 },
{ "Black", 0,0,0 } });
Observe the +
before the lambda definition: convert the lambda in a good-old function pointer.
A little improved version of makeSetFromCmp()
(with a third allocator template argument with a default value and with forwarding) could be
template <typename Key, typename A = std::allocator<Key>>
auto makeSetFromCmp (bool(*cmp)(Key const &, Key const &),
std::initializer_list<Key> && il,
A && all = A{})
{ return std::set(std::forward<std::initializer_list<Key>>(il),
cmp,
std::forward<A>(all)); }