我可以使用 std::set 构造函数指定比较器而不指定所有模板参数吗

Can I specify comparator using std::set constructor without specifying all the template arguments

我想用 lambda 比较器构建一个集合。 由于已知的限制,您不能将 lambda 指定为模板参数(您需要 decltype() 它)所以我考虑在模板参数列表中指定映射的键,在构造函数参数中指定比较器。 类似于:

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

但是据我从错误消息中了解到,一旦我指定了模板参数(<Color>),我就强迫其他人默认(std::less 用于比较器)。并且仅采用比较器的映射构造函数不够智能,无法从比较器参数中获取键类型,也就是这不起作用:

std::set colors([](const Color& a, const Color& b){return a.name()<b.name();});

有没有办法指定我想要一组 Color,但让构造函数指定比较器。

请注意,自 C++17 以来,我可以使用构造函数推导模板类型,但它并不漂亮,因为我需要编写的内容比我想要的多得多。

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/*???*/>{});

完整代码here:

如果我没记错的话,推导指南(在 C++17 中)是不可能显式模板类型并推导其他模板类型的。

如果你想从 lambda 比较器中推导出类型 Color,我能想到的最好的方法是创建一个 makeSetFromCmp() 函数

template <typename Key>
auto makeSetFromCmp (bool(*cmp)(Key const &, Key const &),
                     std::initializer_list<Key> const & il)
 { return std::set(il, cmp); }

诀窍是首先传递比较器,Key 类型可以从比较器中推导出来,因此不需要显式 std::initializer_list<Key> 调用函数。

所以你可以这样写

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

观察 lambda 定义之前的 +:将 lambda 转换为一个很好的旧函数指针。

makeSetFromCmp() 的一个小改进版本(带有默认值和转发的第三个分配器模板参数)可能是

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