这里,x 是 std::vector<int> 还是 std::vector<std::vector<int>>?
这里的其他答案解决了您的其他问题,但我想更彻底地解决这个问题。当我们进行类模板参数推导时,我们从构造函数中synthesize a bunch of function templates,然后从deduction guides 中进行更多操作,并执行重载决策以确定正确的模板参数。
std::vector<T,A> 有很多构造函数,但其中大多数都没有提到 T,这会使 T 成为非推导上下文,因此在此重载中不是一个可行的选择。如果我们预先修剪该集合以仅使用可能可行的集合:
template <class T> vector<T> __f(size_t, T const& ); // #2
template <class T> vector<T> __f(vector<T> const& ); // #5
template <class T> vector<T> __f(vector<T>&& ); // #6, NB this is an rvalue ref
template <class T> vector<T> __f(initializer_list<T> ); // #8
还有这个deduction guide,我也会通过删除分配器来简化它:
template <class InputIt>
vector<typename std::iterator_traits<InputIt>::value_type> __f(InputIt, InputIt );
这些是我们的 5 名候选人,我们正在超载,就像 [dcl.init] 一样,通过__f({v.begin(), v.end()}) 调用。因为这是列表初始化,所以我们 start with the initializer_list candidates 并且只有在没有任何候选者的情况下,我们才会继续处理其他候选人。在这种情况下,有一个initializer_list 候选者是可行的(#8),所以我们选择它而不考虑其他任何一个。该候选将T 推导出为std::vector<int>::iterator,因此我们然后重新启动重载解析过程以选择具有两个迭代器的列表初始化std::vector<std::vector<int>::iterator> 的构造函数。
这可能不是我们想要的结果——我们可能想要一个vector<int>。解决方案很简单:使用()s:
std::vector x(v.begin(), v.end()); // uses explicit deduction guide
现在,我们不进行列表初始化,因此 initializer_list 候选者不是可行的候选者。结果,我们通过推导指南(唯一可行的候选者)推导vector<int>,并最终调用它的迭代器对构造函数。这具有实际使评论正确的副作用。
这是使用{} 初始化与使用() 初始化完全不同的许多地方之一。有人认为{} 是统一初始化——这样的例子似乎可以反驳。我的经验法则:当你特别有意识地需要{} 提供的行为时,使用{}。 () 否则。