Reputation: 7667
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
Upvotes: 4
Views: 1495
Reputation: 35459
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.
Upvotes: 5
Reputation: 219245
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.
Upvotes: 9