Skip to content

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

Open in Compiler Explorer ↗ Quiz this entry