initializer_list 和模板类型推导
Posted
技术标签:
【中文标题】initializer_list 和模板类型推导【英文标题】:initializer_list and template type deduction 【发布时间】:2012-09-08 00:43:52 【问题描述】:考虑函数:
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');
【问题讨论】:
【参考方案1】:您的第一行 printme('a', 'b', 'c')
是非法的,因为无法推断模板参数 T
。如果您明确指定模板参数,它将起作用,例如printme<vector<char>>('a', 'b', 'c')
或 printme<initializer_list<char>>('a', 'b', 'c')
。
您列出的其他是合法的,因为参数具有明确定义的类型,因此可以很好地推导出模板参数T
。
您的带有auto
的sn-p 也有效,因为il
被认为是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<>
如果初始化器是 braced-init-list (8.5.4),则为
std::initializer_list<U>
。
【讨论】:
+1 我学到了一些东西。将std::initialiser_list<>
提升为普通库函数之外的东西。
只是为了完整性。这是解决问题的方法:pastebin.com/huEGwnDt
我们知道为什么会出现这种情况吗?对我来说似乎很奇怪,如果我想允许模板函数(可能是基于范围的算法)采用初始化列表参数,我必须为 std::initializer_list
提供重载。
@JosephMansfield 我还没有找到明确的答案,但我怀疑这与用于统一初始化的大括号有关。调用g(1, 2, 3)
也可以表示g(Foo(1, 2, 3))
,其中Foo
是任何类,其构造函数采用三个整数。
@4ZM 如果不带参数调用,调用有歧义。【参考方案2】:
您还可以重载该函数以显式采用 initializer_list 类型的参数。
template<typename T>
void printme(std::initializer_list<T> t)
for (auto i : t)
std::cout << i;
【讨论】:
当然,但这会使其他版本失败,例如printme(std::vector<char>('a', 'b', 'c'));
。不幸的是,模板专业化在这里不起作用。
哦,那太好了!谢谢你。我以为我已经尝试过了,但我错了。模板专业化在这里工作得很好。由于该函数可以完全以相同的方式实现,剩下的就是如何弄清楚如何让其中一个调用另一个......
工作正常。您甚至可以通过为 initializer_list 实现完美转发来改进该解决方案,如下所示:pastebin.com/1ttGniBH?【参考方案3】:
这在 § 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 );
【讨论】:
以上是关于initializer_list 和模板类型推导的主要内容,如果未能解决你的问题,请参考以下文章