20 Beginner The Hundred
The Most Vexing Parse
the object that never existed
#include <iostream>
struct Timer {
Timer() { std::cout << "timer started\n"; }
};
int main() {
Timer t(); // create a timer with the default constructor... right?
}
Run it. What does it print?
Answer
Nothing. No timer is ever created — the constructor never runs.
Why¶
C++ has a blunt disambiguation rule: anything that can be parsed as a declaration is a
declaration. Timer t(); matches the syntax of a function declaration — a function
named t, taking no parameters, returning a Timer — so that is what the compiler sees.
There is no object, and any later t.stop() would fail to compile with a baffling error
about a function not having members. The trap bites hardest when the arguments are
themselves types: Widget w(Gadget()); also declares a function.
GCC and Clang can warn about this (-Wvexing-parse; GCC folds it into -Wall).
The fix¶
Drop the parentheses, or use braces — a braced initializer can never be parsed as a function declaration:
Timer t; // object, default-constructed
Timer t{}; // object, default-constructed — immune to the vexing parse
Takeaway: if it can be a function declaration, it is one — initialize with {}
when in doubt.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo