I came upon a similar piece of code during an IRC discussion. While I am certain that some may consider this example trivial, I admit that the correct answer eluded me even after I verified the result with the compiler – it wasn’t until I checked the standard that it became clear.
Consider the following class:
struct foo { foo() { cout << __PRETTY_FUNCTION__ << endl; } foo(int) { cout << __PRETTY_FUNCTION__ << endl; } }; |
And now, consider the following code:
int global; int main() { foo(); foo(42); foo(global); } |
Would you care to guess what will be printed?
Well, the answer is:
foo::foo() foo::foo(int) foo::foo() |
[link]
The line foo(global); doesn’t cause the foo::foo(int) constructor to run, instead causing the default one to be executed. Why is that?
The answer is fairly simple, if vexing, as was spoilt in the title: the C syntax rules, that C++ was saddled with, declare that
N4140 § 6.8 [stmt.ambig] / 1
An expression-statement with a function-style explicit type conversion ([expr.type.conv]) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.
It then follows with an example that depicts a nearly identical problem:
class T { // ... public: T(); T(int); T(int, int); }; T(a); // declaration
This is exactly the same principle that allows the Most Vexing Parse to exist, and that drove the standards committee to design mostly uniform initialiation added in C++11 (and made even less uniform in C++17).
In this case, it meant that foo(global); declared a new local variable called global, shadowing the global global variable. The parentheses here were entirely optional and the meaning of this declaration was identical to foo global;.
int global; int main() { foo(); foo(42); foo global; // the same thing! } |
That is also the reason why the default constructor was executed.
All in all, this whole situation could be avoided with the uniform initialization:
foo{global}; // calls foo:foo(int) foo(global); // declares global as a local variable, initializes calling // foo::foo() |
`-Wshadow` to the rescue :)
Nice example. Clang at least prompts a warning about this: https://wandbox.org/permlink/KiASSzWytrn65Y1w
VS like GCC do not warn http://rextester.com/IGS50846