I have just watched Chandler's presentation on Clang at Going Native 2012. He presents the following code:
#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);
}
Chandler states that this calls f(void*) for c++11 and f(X) for c++03.
He also states that the reason is that S().n is default initialised to 0, making it a nullptr constant.
Firstly am I right in assuming that the zero initialization of member variable n is compiler implementation dependent and NOT guaranteed by the standard (or did this change with c++11)? Chandler hints this is due to support for constant expressions but I still cannot fully follow his reasoning.
Secondly why would f(X) be called with C++03 and not c++11? I would of assumed that f(void*) would kick in regardless of the value of S().n over an implicit conversion to X
For Chandler's explanation see the following link, 45 minutes in:
Clang: Defending C++ from Murphy's Million Monkeys
Firstly am I right in assuming that the zero initialization of member variable n is compiler implementation dependent and NOT guaranteed by the standard (or did this change with c++11)?
No, S() means value initialize in both C++03 and C++11. Though I believe the wording for this is much clearer in C++11 than C++03. In this case, value-initialization forwards to zero-initialization. Contrast this with default-initialization which does not zero:
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
Secondly why would f(X) be called with C++03 and not c++11? I would of assumed that f(void*) would kick in regardless of the value of S().n over an implicit conversion to X
In C++03 an int can never become a null pointer constant. Once 0 is typed as an int, say by assigning it to an int, then it is forever an int, and not a null pointer constant.
In C++11, S().n is implicitly a constexpr expression with value 0, and a constexpr expression with value 0 can be a null pointer constant.
Finally, this is not an intentional change on the part of the committee as far as I can tell. If you're writing code that depends on this difference, you may well get bitten in the future if/when the committee corrects itself. I would steer well clear of this area. Use it to win bar bets -- not in production code.
First, some clarification on the rules of initialization for both C++03 and 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
Then, remember that int is not convertible to void* so in C++03 f(S().n) will never call void f(void*); even if no other declaration of f is available.
What is possible in C++03 is to do f(0), which will call void f(void*); even if void f(X); is present. The reason for this is that the int -> X conversion (a so called user-defined conversion) is not preferred to the zero integral constant -> void* conversion (which isn't a UD conversion). Note that it's also possible to call void f(void*); via f( (int()) ) because int() is also a zero integral constant, even in C++03. (As usual the brackets are here to resolve a syntactical ambiguity.)
What C++11 changes is that now S().n is a zero integral constant. The reason for this is that S() is now a constant expression (thanks to generalized constant expression), and this kind of member access also is.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With