我刚看过Chandler在2012年Going Native上对Clang的演讲。他提供了以下代码:
#include <iostream>
struct S{ int n; };
struct X{ X(int) {}; };
void f( void* )
{
std::cerr << "Pointer!\n";
}
void f( X )
{
std::cerr << "X!\n";
}
int main()
{
f(S().n);
}
钱德勒表示,这为c ++ 11调用f(void*)
,为c ++ 03调用f(X)
。
他还说,原因是S()。n默认初始化为0,使其成为nullptr
常数。
首先,我正确地假设成员变量n的零初始化是依赖于编译器实现并且不是由标准保证(或者使用c ++ 11进行了此更改)?钱德勒暗示这是因为支持不断的表达,但我仍然不能完全遵循他的推理。
其次,为什么用C ++ 03而不是c ++ 11调用f(X)
?我假设f(void*)
无论S().n
的值是否隐含转换为X
对于Chandler的解释,请参阅以下链接:45分钟:
答案 0 :(得分:9)
首先,我正确地假设成员的零初始化 变量n依赖于编译器实现,不受保证 标准(或用c ++ 11做了这个改变)?
不,S()
表示在C ++ 03和C ++ 11中初始化值。虽然我认为C ++ 11中的措辞比C ++ 03更清晰。在这种情况下,值初始化转发到零初始化。将此与默认初始化(不为零)进行对比:
S s1; // default initialization
std::cout << s1.n << '\n'; // prints garbage, crank up optimizer to show
S s2 = S(); // value initialization
std::cout << s2.n << '\n'; // prints 0
其次为什么用C ++ 03而不是c ++ 11调用f(X)?我会的 假设无论S()的值如何,f(void *)都会启动 通过隐式转换为X
在C ++ 03中,int
永远不会成为空指针常量。将0
输入为int
后,通过将其分配给int
,则它永远是int
,而不是空指针常量。
在C ++ 11中,S().n
隐式地是constexpr
表达式,其值为0
,而constexpr
表达式的值为0
可以为null指针常数。
最后,就我所知,这不是委员会的故意改变。如果您正在编写依赖于这种差异的代码,那么如果/当委员会纠正自己时,您可能会在将来被咬。我会很清楚这个领域。用它来赢得赌注 - 而不是生产代码。
答案 1 :(得分:5)
首先,对C ++ 03和C ++ 11的初始化规则进行了一些澄清:
// This is default construction
S s;
// s.i has undefined value
// This is initialization from a value-initialized S (C++03 rules)
S s = S();
// s.i has been zero-initialized
// This is value initialization (C++11 rules)
// new syntax, better rules, same result
S s {};
// s.i has been zero-initialized
然后,请记住int
无法转换为void*
所以在C ++ 03 f(S().n)
中永远不会调用void f(void*);
即使没有可以使用f
的其他声明。
C ++ 03中可能的 是f(0)
,即使void f(void*);
存在,也会调用void f(X);
。原因是int
- &gt; X
转换(所谓的用户定义转换)不优于零积分常数 - &gt; void*
转换(不是UD转换)。请注意,也可以通过void f(void*);
调用f( (int()) )
因为int()
也是零积分常量,即使在C ++ 03中也是如此。 (像往常一样,括号在这里解决了语法模糊性。)
C ++ 11的变化是现在S().n
是零积分常数。原因是S()
现在是一个常量表达式(感谢通用常量表达式),这种成员访问也是。