0

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 Colors, 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:

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277

1 Answers1

2

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)); }
max66
  • 65,235
  • 10
  • 71
  • 111
  • ah, I was afraid of that... BTW I think boost callable traits could enable you to remove the need for + ( you could pass callable and metahack to get the argument type) but in general this answer is very good... so if nobody comes up with a better one soon I will accept it. – NoSenseEtAl Jan 08 '18 at 18:52