我理解,如果使用支持的初始值设定项,auto
将推导出std::initializer_list
类型,而模板类型推导将失败:
auto var = { 1, 2, 3 }; // type deduced as std::initializer_list<int>
template<class T> void f(T parameter);
f({ 1, 2, 3 }); // doesn't compile; type deduction fails
我甚至知道在C ++ 11标准中指定的位置:14.8.2.5/5 bullet 5:
[如果程序有,这是一个非推导的上下文]一个函数参数,其关联参数是初始化列表(8.5.4)但参数 没有std :: initializer_list或对可能是cv-qualified的std :: initializer_list的引用 类型。 [示例:
模板void g(T);
克({1,2,3}); //错误:没有推断出T
的参数- 结束示例]
我不知道或理解的是为什么存在类型演绎行为的这种差异。 C ++ 14 CD中的规范与C ++ 11中的规范相同,因此标准化委员会可能不会将C ++ 11行为视为缺陷。
有人知道为什么auto
推断出支撑初始值设定项的类型,但是不允许使用模板吗?虽然对“这可能是原因”形式的推测性解释很有意思,但我特别感兴趣的是那些知道为什么标准是按原样编写的人的解释。
答案 0 :(得分:13)
模板不做任何演绎有两个重要原因(我在与负责人的讨论中记得两个)
对未来语言扩展的担忧(你可以发明多种含义 - 如果我们想要为支持的初始化列表函数参数引入完美的转发呢?)
大括号有时可以有效地初始化依赖的函数参数
template<typename T> void assign(T &d, const T& s);
int main() {
vector<int> v;
assign(v, { 1, 2, 3 });
}
如果T
在右侧推导到initializer_list<int>
但在左侧推导到vector<int>
,则由于矛盾的参数推断而无法推断。
auto
对initializer_list<T>
的扣除是有争议的。存在针对C ++的提议 - 在14之后删除它(并禁止使用{ }
或{a, b}
进行初始化,并使{a}
推导为a
的类型)
答案 1 :(得分:1)
原因在N2640中进行了描述:
{}列表无法推导纯类型参数
T
。例如:template<class T> void count(T); // (1). struct Dimensions { Dimensions(int, int); }; size_t count(Dimensions); // (2). size_t n = count({1, 2}); // Calls (2); deduction doesn't // succeed for (1).
另一个例子:
template<class T> void inc(T, int); // (1) template<class T> void inc(std::initializer_list<T>, long); // (2) inc({1, 2, 3}, 3); // Calls (2). (If deduction had succeeded // for (1), (1) would have been called — a // surprise.)
另一方面,能够为
initializer_list<X>
推论T
对于 允许:auto x = { 1, 1, 2, 3, 5 }; f(x); g(x);
自从EWG关于 初始化程序列表。
我们现在没有为参数类型
T
和{}列表(我们在本文前面的草图和草稿中追求的一种选择)匹配的巧妙的推导规则,而是选择使用当初始化程序为{} -list时,“自动”变量推导的一种特殊情况。即,对于使用“自动”类型说明符和{} -list初始化程序声明的变量的特定情况,“自动”推导是针对函数f(initializer_list<T>)
而不是针对函数{{1} }。
总而言之,问题在于,如果我们允许{} -list推断出普通类型参数f(T)
,那么在重载解析期间,带有参数T
的函数将具有很高的优先级,可能会导致有线行为(如上述示例)。
答案 2 :(得分:0)
首先,它是“形式的推测性解释”,这可能就是你所说的“”原因。
{1,2,3}
不仅是std::initializer_list<int>
,还允许在没有构造函数的情况下初始化类型。例如:
#include <initializer_list>
struct x{
int a,b,c;
};
void f(x){
}
int main() {
f({1,2,3});
}
是正确的代码。要表明它不是initializer_list
,请看下面的代码:
#include <initializer_list>
struct x{int a,b,c;};
void f(x){
}
int main() {
auto il = {1, 2, 3};
f(il);
}
错误是:
prog.cpp: In function ‘int main()’:
prog.cpp:10:9: error: could not convert ‘il’ from ‘std::initializer_list<int>’ to ‘x’
现在问题是“有什么区别?”
在auto x = {1, 2, 3};
代码中确定类型是可以的,因为编码器使用auto
在功能模板的情况下,他可能确定他使用的是不同的类型。并且防止模糊情况下的错误是好的(它看起来不像C ++风格)。
特别糟糕的是,当有1个函数f(x)
然后它被更改为模板1时。程序员写道将其用作x
,并在为其他类型添加新函数后稍微改变以调用完全不同的函数。