为什么C++17标准没有引入部分类模板参数推导?

14
我希望我能够在构建std::set/ std::map或任何其他使用自定义比较器的容器时,利用新的模板参数推导功能。我的目标是创建一个一行代码的语句,它将创建一个具有lambda比较器的高效集合。
自C++11以来我可以做到的是:
std::set<int, std::function<bool(int, int)>> s([](int a, int b) {return a > b;});

但由于它使用了std::function,因此速度明显较慢。

另一个选择是:

auto mycomp = [](int a, int b) {return a > b; };
std::set<int, decltype(mycomp)> s(mycomp);
它能够完成工作,但是1)需要2行代码并创建mycomp变量,2)我需要显式地传递mycomp的类型。据我查阅参考页面,标准容器中没有为这种情况提供推断指南。不幸的是,我担心即使在当前语言标准(C++17)下也无法做到这一点,因为可以找到以下内容:只有在没有模板参数列表的情况下才执行类模板参数推导。如果指定了模板参数列表,则不进行推导。这背后的原因是什么?为什么他们不允许部分参数推导?我想我忽略了一些问题,但在我看来,这将非常有帮助。

1
部分推导最初在提案中,但已被移除。请参见c17-class-template-partial-deduction - Jarod42
@Kaznov:“我的目标是创建一个一行语句,使用lambda比较器创建高效的集合。” ……为什么这是你的目标?直接创建一个带有operator()重载的结构体不好吗?为什么你必须(甚至想要)使用lambda? - Nicol Bolas
4个回答

11
作为替代方案,您仍然可以使用旧的make_xxx
template <typename T, typename COMP>
std::set<T, COMP> make_set(COMP comp)
{
    return std::set<T, COMP>{comp};
}


auto s = make_set<int>([](int a, int b) {return a > b; });

11
在一行中(C++17)
std::set s(std::initializer_list<int>{}, [](int a, int b) {return a > b; });

谢谢!我不知道这个构造函数的存在。好吧,它解决了容器的问题。 - Kaznov
10
@Kaznov的做法能够实现,但并不十分优雅:那个std::initializer_list<int>{}只是为了明确指定int类型,看起来非常丑陋。 - max66

5
你应该编写一个比较器函数对象类型,这样在使用集合时就不需要代理了。虽然会增加很多代码(好丑!),但几乎在所有方面都更好:
struct MyComparator
{
    bool operator()(int a, int b) const { ...; }
};
using MySet = std::set<int, MyComparator>;

从那时起,您只需在需要的位置创建自定义排序集即可。

我知道这是解决这个问题的最佳方式,但我正在寻找最紧凑的方法。 - Kaznov
1
@Kaznov:为什么需要紧凑?在模板特化的中间放置一个大的lambda并不能使您的代码更易于阅读。这种不太“紧凑”的写法比lambda更容易阅读。 - Nicol Bolas
我正在寻找最快的方法来完成这个任务,以便在算法竞赛中使用。在那里,你不关心代码的外观,越短越好。 - Kaznov

0
由于存在默认值的模板参数会带来歧义,特别是在可变参数模板的情况下。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接