在现代C ++中,以下编译并按预期运行:
for(const auto& x : {"hello", "nice", "world"}) {
cout << x << endl;
}
但是如果初始化列表包含不同类型的元素,我就不能再编译代码了:
for(const auto& x : {"hello", 123, '4'}) {
cout << x << endl;
}
为什么不编译运行?这个初始化列表不能被理解为3 - tuple
吗?或experimental::any
s的初始化列表?还是什么?
备注:
--std=c++14
; --std=c++17
不会改变任何内容。<experimental/any>
。unable to deduce ‘std::initializer_list<auto>&&’ from ‘{"hello", 123, '4'}’
。答案 0 :(得分:7)
这个初始化列表不能被理解为3元组吗?或者是实验:: anys的初始化列表?还是什么?
没有
braced-init-list没有类型,也不能推断为类型。它可用于初始化某些东西......但为了实现这一点,编译器必须知道“某事”是什么。
在基于范围的for
语句中,编译器生成此定义的等效项:
auto && __range = {"hello", 123, '4'} ;
但这不是有效的C ++,因为braced-init-list的类型未知。
或
experimental::any
的初始化列表?
它不能是std::experimental::any
,因为它不在标准中。你不能让编译器神奇地将某些东西解释为在其他规范中定义的库类型。你需要一个编译器已知的内置any
类型才能工作,那就是永远不会被委员会接受(由于动态分配和类型擦除导致的间接性,使用any
会有成本,更不用说你不能做一些有用的事情了for
循环而不知道类型。)
如果你想要一个any
的序列,那么这样说:
for (auto i : std::initializer_list<std::experimental::any>{ a, b, c})
但是你在循环体中放了什么?您必须知道每个元素的类型才能做任何有用的事情,如果您已经知道有多少元素及其所有类型,为什么还要使用循环?循环用于重复执行相同的操作,而不是对几种不同的类型执行多个不同的操作。
我建议更好的办法是:
auto op = [](auto i) { /* do something with i */ }
op(a);
op(b);
op(c);
对于创建元组(不是std::tuple
,但是动态生成的匿名结构,类似于lambdas创建匿名函数的方式),内置语言支持肯定会很好,但它肯定不会涉及间接通过any
。
你可以使用lambdas模拟类似的东西:
auto anon_struct = [a, b, c](auto func) { func(a); func(b); func(c); }
这会创建一个捕获a
,b
和c
的lambda,并允许您使用将应用于每个变量的函数调用它:
auto op = [](auto i) { /* do something with i */ }
anon_struct(op); // apply op to each "member" of the anon struct
或者更有用的是:
auto anon_struct = [a, b, c](auto visitor) { visitor(a, b, c); }
auto visitor1 = [](auto i, auto j, auto k) { /* do something with i */ }
auto visitor2 = [](auto i, auto j, auto k) { /* do something with j */ }
anon_struct(visitor1);
anon_struct(visitor2);
答案 1 :(得分:1)
在标准草案 18.9 / p1初始化程序列表[support.initlist] 中,初始化程序列表类定义为:
template<class E> class initializer_list { ... }
因此,当您实际提供具有不同类型的聚合初始化列表时,问题是,编译器必须选择哪一种类型来推导E
?
答案 2 :(得分:0)
初始化列表元素的相关类型将是std::experimental::any
- 但这实际上并不是语言标准的一部分,因此编译器在推断类型时无法“了解它”。如果/当它在标准中时,那么我们可能希望代码片段能够正常工作。