我刚看了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);
}
Run Code Online (Sandbox Code Playgroud)
Chandler声称这需要f(void*)c ++ 11和f(X)c ++ 03.他还说,原因是S().n默认初始化为0,使其成为nullptr常数.
首先,我正确地假设成员变量n的零初始化是依赖于编译器实现的并且不是由标准保证(或者用c ++ 11进行了这种改变)?钱德勒暗示这是由于对持续表达的支持,但我仍然不能完全遵循他的推理.
其次,为什么f(X)要用C++ 03而不是c ++ 11调用?我认为f(void*)无论S().n隐含转换的价值如何,都会启动X
对于Chandler的解释,请参阅以下链接,45分钟:
首先,我正确地假设成员变量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
Run Code Online (Sandbox Code Playgroud)
其次,为什么用C++ 03而不是c ++ 11调用f(X)?我假设f(void*)无论S().n的值是否隐式转换为X,都会启动
在C++ 03中,int永远不能成为空指针常量.一旦0输入为a int,比如将其分配给a int,那么它永远是一个int,而不是一个空指针常量.
在C++ 11中,S().n隐式地是constexpr具有值0的constexpr表达式,具有值的表达式0可以是空指针常量.
最后,就我所知,这不是委员会的故意改变.如果您正在编写依赖于这种差异的代码,那么如果/当委员会纠正自己时,您可能会在将来被咬.我会很清楚这个领域.用它来赢得赌注 - 而不是生产代码.
首先,对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
Run Code Online (Sandbox Code Playgroud)
然后,请记住,int是无法转换为void*所以在C++ 03 f(S().n)将永远不会调用void f(void*);即使没有其他的声明f可用.
什么是用C++ 03可能是做f(0),这将调用void f(void*);,即使void f(X);是存在的.原因是int- > X转换(所谓的用户定义转换)不优于零积分常数 - > void*转换(不是UD转换).注意,也可以调用void f(void*);via,f( (int()) )因为int()它也是一个零积分常数,即使在C++ 03中也是如此.(像往常一样,括号在这里解决了语法模糊性.)
C++ 11的变化是现在S().n是零积分常数.原因是它S()现在是一个常量表达式(归功于广义常量表达式),这种成员访问也是如此.