Question

C++ Most Vexing Parse: Why `Foo foo2()` Is Not an Object

Question

Why does this C++ program fail when creating foo2 with the no-argument constructor, even though creating foo1 with the one-argument constructor works?

class Foo {
public:
    Foo() {}
    Foo(int a) {}

    void bar() {}
};

int main() {
    // Works: creates a Foo object using Foo(int).
    Foo foo1(1);
    foo1.bar();

    // Fails: why is this not a Foo object?
    Foo foo2();
    foo2.bar();

    return 0;
}

The compiler reports an error similar to:

request for member 'bar' in 'foo2', which is of non-class type 'Foo ()()'

Why does this happen, and how can a Foo object be correctly created with its no-argument constructor?

Short Answer

You will learn about the C++ most vexing parse: a grammar rule that makes Foo foo2(); a function declaration rather than construction of a Foo object. You will also learn the clearest modern ways to default-construct objects.

Concept

C++ allows declarations of both objects and functions to use similar syntax. When a statement can be interpreted in more than one valid way, C++ applies a historical parsing rule:

If something can be parsed as a function declaration, it is parsed as a function declaration.

Therefore, this line:

Foo foo2();

does not create a Foo object. It declares a function named foo2 that:

  • takes no parameters, and
  • returns a Foo.

No function body is supplied, but declarations without definitions are allowed. The error occurs on the next line because foo2 is a function, not an object. A function has no member function named bar().

This confusing situation is traditionally called the most vexing parse. It matters because C++ code may look like object construction while silently declaring a function instead. Understanding it helps you read compiler diagnostics and choose unambiguous initialization syntax.

Mental Model

Think of C++ as reading a form that can describe either a thing or a recipe for making a thing.

  • Foo foo2; means: “Put a Foo object named foo2 here.”
  • Foo foo2(); means: “Declare a recipe named foo2 that takes no ingredients and produces a Foo.”

Even though empty parentheses look like “call the default constructor,” C++ treats them as part of a function declaration whenever that interpretation is possible.

To create the object, leave the parentheses out, or use braces.

Take Quiz

Syntax and Examples

Use one of these unambiguous forms to call a default constructor.

Foo foo1;     // Default initialization: calls Foo::Foo()
Foo foo2{};   // Value/list initialization: calls Foo::Foo()

Complete example:

#include <iostream>

class Foo {
public:
    Foo() {
        std::cout << "Default constructor\n";
    }

    Foo(int a) {
        std::cout << "Constructor argument: " << a << "\n";
    }

    void bar() {
        std::cout << "bar called\n";
    }
};

int main() {
    Foo first(1);  // Calls Foo(int)
    first.bar();

    Foo second;    // Calls Foo()
    second.bar();

    Foo third{};   // Also calls Foo()
    third.bar();
}

Foo second; is the traditional and simple form for a local object. Foo third{}; is also widely used in modern C++ because braces avoid several ambiguous parsing cases.

The following remains a function declaration:

Foo notAnObject();

Step by Step Execution

Consider this code:

class Foo {
public:
    Foo() {}
    void bar() {}
};

int main() {
    Foo foo2();
    foo2.bar();
}

Step by step:

  1. The compiler reads Foo foo2();.
  2. It sees a valid function-declaration pattern: return_type function_name(parameter_list).
  3. It interprets the line as: “Declare a no-argument function called foo2 that returns Foo.”
  4. No Foo instance is created, and Foo::Foo() is not called.
  5. The compiler reads foo2.bar().
  6. foo2 is known as a function, not an instance of Foo.
  7. Calling .bar() on a function is invalid, so compilation fails.

Corrected version:

int main() {
    Foo foo2;      // A Foo object is created here.
    foo2.bar();    // Valid member-function call.
}

Real World Use Cases

Default construction is common when an object can begin in a valid empty or initial state.

  • Configuration objects: Start with defaults, then change selected settings.

    Config config{};
    config.enableLogging = true;
    
  • Streams and buffers: Create an empty object and fill it later.

    std::string message;
    message += "Ready";
    
  • Data models: Construct an object before receiving data from a file, form, or API.

    User user{};
    // Populate user after parsing input.
    
  • Containers: Create an empty container before adding elements.

    std::vector<int> scores;
    scores.push_back(95);
    

In each case, use Type name; or Type name{};, not Type name();, when you intend to create an object.

Take Quiz

Real Codebase Usage

In real C++ projects, developers usually avoid empty parentheses for local default construction entirely.

Prefer clear initialization

Session session{};
std::vector<int> ids;

Braces are especially helpful in generic code, where parenthesized initialization can become ambiguous.

Construct directly with required dependencies

If an object requires valid input, give it a constructor argument instead of allowing an incomplete default state.

class DatabaseConnection {
public:
    explicit DatabaseConnection(const std::string& connectionString);
};

DatabaseConnection db{"server=localhost;database=app"};

Use factory functions for complex setup

A factory function can validate input and return a ready-to-use object.

Foo makeFoo() {
    return Foo{};
}

Foo foo = makeFoo();

Use guard clauses after construction when validation is separate

Request request{};

if (!request.isValid()) {
    return;
}

process(request);

The important rule remains the same: a local declaration written as Type name(); declares a function, so do not use that spelling to construct an object.

Common Mistakes

Writing empty parentheses for a default constructor

Broken code:

Foo foo();
foo.bar();

This declares a function. Fix it with either:

Foo foo;
// or
Foo foo{};

Assuming the compiler will call the default constructor later

This declaration does not create an object now or later:

Foo createFoo();

It only announces that a function with that name exists. If you later try to call it, it must have a definition:

Foo createFoo() {
    return Foo{};
}

int main() {
    Foo foo = createFoo();
}

Confusing a function declaration with a function call

A function call needs an expression to call. For example:

Foo makeFoo() {
    return Foo{};
}

int main() {
    Foo foo = makeFoo(); // Calls makeFoo.
}

Here makeFoo() is on the right side of an assignment, so it is clearly an expression and therefore a call.

Treating {} and () as always interchangeable

For default construction, Foo foo; and Foo foo{}; often have the same practical result for class types. However, braces enforce list-initialization rules and can reject narrowing numeric conversions.

int value{3.7}; // Error: narrowing conversion
int other(3.7); // Allowed; value becomes 3

Choose braces when their stricter conversion rules are desired; do not use () with no arguments for a local object.

Comparisons

SyntaxWhat it means for a class typeCreates an object?
Foo foo;Default-initializes foo; calls Foo() when it existsYes
Foo foo{};List/value-initializes foo; calls Foo() when it existsYes
Foo foo(1);Direct-initializes foo with 1; calls Foo(int)Yes
Foo foo = Foo{};Creates from a temporary Foo{}; usually optimizedYes
Foo foo();Declares a function foo returning Foo with no parametersNo

Parentheses versus braces

FormStrengthImportant behavior
Type object(args);Traditional direct initializationCan participate in parsing ambiguities in more complex declarations
Type object{args};Modern list initializationAvoids the empty-parentheses issue and rejects narrowing conversions
Type object;Simple default constructionClear and idiomatic for a default-constructed local object

For this specific issue, the two safe choices are:

Foo foo;
Foo foo{};
Take Quiz

Cheat Sheet

// Default-construct a local object
Foo foo;
Foo foo{};

// Construct with an argument
Foo foo(42);
Foo foo{42};

// Do NOT use this to create an object
Foo foo(); // Declares: Foo foo() -- a function returning Foo
  • C++ parses an ambiguous declaration as a function declaration when possible.
  • Type name(); is a no-parameter function declaration, not default construction.
  • Use Type name; for traditional default construction.
  • Use Type name{}; for unambiguous brace initialization.
  • The compiler message mentioning a non-class type such as Foo ()() is a strong clue that you declared a function accidentally.

FAQ

Why is Foo foo2(); a function declaration in C++?

It matches valid function-declaration syntax: it declares foo2 as a function taking no parameters and returning Foo. C++ chooses that interpretation when the syntax is ambiguous.

How do I call a default constructor in C++?

For a local object, write either:

Foo foo;
Foo foo{};

Both construct a Foo using its no-argument constructor when one is available.

Is Foo foo; the same as Foo foo{};?

For many class types with a user-provided default constructor, they have the same result. However, initialization details differ for fundamental types and aggregate types. Braces also reject narrowing conversions.

What does “most vexing parse” mean?

It is the informal name for cases where C++ syntax that looks like object construction is parsed as a function declaration instead.

Why does the error say Foo ()()?

That type description represents a function taking no arguments and returning Foo. It reveals that the compiler interpreted foo2 as a function.

Can I write Foo foo = Foo(); instead?

Yes, it creates an object, but it is unnecessarily verbose. Prefer Foo foo; or Foo foo{};.

Does this issue occur with constructors that take arguments?

The exact empty-parentheses problem does not occur with Foo foo(1);, because that is an object declaration using a constructor argument. More complicated parenthesized declarations can still produce related ambiguities.

Take Quiz

Mini Project

Description

Build a small Task class that supports both default construction and construction with a task name. The program will create tasks safely, display them, and demonstrate the difference between correct object initialization and the most vexing parse.

Goal

Create and use default-constructed and argument-constructed Task objects without accidentally declaring a function.

Requirements

Use a Task class with a default constructor and a constructor that accepts a task name. Add a member function that prints the task name and completion status. Create one task with default construction using braces or no parentheses. Create one task by passing a name to its constructor. Call the display member function on both objects.

Take Quiz

Keep learning

Related questions

View more