Default constructors, initialization of POD and implicit type conversions in C ++ 11

I just watched Chandler's presentation at Clang at Going Native 2012. It 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 claims that this calls f(void*) for C ++ 11 and f(X) for C ++ 03. He also claims that the reason is that S (). N is initialized to 0 by default, which makes it a nullptr constant.

First of all, I am right in assuming that the zero initialization of the member variable n is dependent on the implementation of the compiler and is NOT guaranteed by the standard (or is this change using C ++ 11)? Chandler hints that this has to do with constant expression support, but I still can't fully follow his reasoning.

Secondly, why would f(X) be called with C ++ 03 rather than C ++ 11? I would suggest that f(void*) would kick regardless of the value of S().n by implicit conversion to X

For Chandler's explanation, see the following link: 45 minutes:

Clang: C ++ Protection from Monphy Million Monkeys

+4
source share
2 answers

Firstly, I am right in assuming that the zero initialization of the member variable n is dependent on the implementation of the compiler and the standard is NOT guaranteed (or has it changed using C ++ 11)?

No, S() means initializing the value in both C ++ 03 and C ++ 11. Although I believe that the wording of this is much clearer in C ++ 11 than C ++ 03. In this case, the initialization of the value goes over to zero initialization. Contrast this with default initialization, which is non-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 is f (X) called with C ++ 03, and not C ++ 11? I would suggest that f (void *) will hit regardless of the value of S (). n over the implicit conversion to X

In C ++ 03, an int never become a null pointer constant. Once 0 is typed as an int , say by assigning it to int , then it is forever an int , and not a constant of a null pointer.

In C ++ 11, S().n implicitly a constexpr expression with a value of 0 , and a constexpr expression with a value of 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 write code that depends on this difference, you can bite in the future if / when the committee recovers. I would do well in this area. Use it to win bets in a bar - not in the production code.

+9
source

Firstly, some refinements to the initialization rules for both C ++ 03 and C ++ 11:

 // This is default construction S s; // si has undefined value // This is initialization from a value-initialized S (C++03 rules) S s = S(); // si has been zero-initialized // This is value initialization (C++11 rules) // new syntax, better rules, same result S s {}; // si has been zero-initialized 

Then remember that int not converted to void* , so in C ++ 03 f(S().n) will never call void f(void*); even if no other ad f is available.

In C ++ 03 it is possible to make f(0) , which will call void f(void*); , even if void f(X); present void f(X); . The reason for this is that the int β†’ X transform (the so-called custom transform) is not preferable for transforming the null integral constant β†’ void* (which is not a UD transform). Note that you can also call void f(void*); via f( (int()) ) , because int() also a null integral constant, even in C ++ 03. (As usual, the brackets are to resolve syntactic ambiguity.)

What is C ++ 11 is that now S().n is a null integral constant. The reason for this is that S() now a constant expression (thanks to the generalized constant expression), and this kind of access to the member also exists.

+5
source

Source: https://habr.com/ru/post/1399160/


All Articles