46

考虑函数:

template<typename T>
void printme(T&& t) {
  for (auto i : t)
    std::cout << i;
}

或任何其他需要具有 begin()/end() 启用类型的参数的函数。

为什么以下是非法的?

printme({'a', 'b', 'c'});

当所有这些都是合法的:

printme(std::vector<char>({'a', 'b', 'c'}));
printme(std::string("abc"));
printme(std::array<char, 3> {'a', 'b', 'c'});

我们甚至可以这样写:

const auto il = {'a', 'b', 'c'};
printme(il);

或者

printme<std::initializer_list<char>>({'a', 'b', 'c'});
4

3 回答 3

50

您的第一行是非法的,因为无法推断printme({'a', 'b', 'c'})模板参数。T如果您明确指定模板参数,它将起作用,例如printme<vector<char>>({'a', 'b', 'c'})or printme<initializer_list<char>>({'a', 'b', 'c'})

您列出的其他参数是合法的,因为参数具有明确定义的类型,因此T可以很好地推导出模板参数。

您的代码片段auto也有效,因为il被认为是 type std::initializer_list<char>,因此printme()可以推断出模板参数。


这里唯一“有趣”的部分是auto会选择类型std::initializer_list<char>,但不会选择模板参数。这是因为 C++11 标准的 § 14.8.2.5/5 明确指出这是模板参数的非推导上下文:

一个函数形参,其关联实参是初始化列表 (8.5.4) 但该形参没有 std::initializer_list 或对可能有 cv 限定的 std::initializer_list 类型的引用。[例子:

template<class T> void g(T);
g({1,2,3}); // error: no argument deduced for T

—结束示例]

然而auto,§ 7.1.6.4/6 明确支持std::initializer_list<>

如果初始化程序是一个花括号初始化列表(8.5.4),则使用std::initializer_list<U>.

于 2012-09-14T20:51:49.847 回答
12

您还可以重载该函数以显式采用 initializer_list 类型的参数。

template<typename T>
void printme(std::initializer_list<T> t) {
  for (auto i : t)
    std::cout << i;
}
于 2012-09-14T23:11:49.787 回答
5

这在 § 14.8.2.5/5 中有明确规定

一个函数参数,其关联参数是一个初始化列表,但该参数没有 std::initializer_list或不引用可能的 cv 限定 std::initializer_list类型。[ 例子:

template<class T> void g(T);
g({1,2,3}); // error: no argument deduced for T

—结束示例]

要使其工作,您可以显式指定模板参数类型。

printme<std::initializer_list<int>>( {1,2,3,4} );
于 2012-09-14T20:33:19.800 回答