Calling class constructor with parameter - request of a member in 'x' which is of non-class type

270 views Asked by At

I have a class A that accepts class B as a constructor parameter. Class B can be constructed from int value. My original code is quite complex, but I hope I've reduced it to the very base case:

class B {
public:
    explicit B(int a) : val(a) {}
private:
    int val;
};

class A {
public:
    A(const B & val) : value(val) {};
    void print() {
        //does nothing
    }

private:
    B value;
};


int main() {
    int someTimeVar = 22;
    A a(B(someTimeVar));
    a.print();
}

And this is the error code I'm getting:

$ g++ test.cpp -Wall -O0
test.cpp: In function ‘int main()’:
test.cpp:22:7: error: request for member ‘print’ in ‘a’, which is of non-class type ‘A(B)’
     a.print();
       ^
test.cpp:20:9: warning: unused variable ‘someTimeVar’ [-Wunused-variable]
     int someTimeVar = 22;
         ^

I use GCC (4.9.2 20150304 (prerelease)), platform: arch linux.

The following modification to the main function compiles fine:

int main() {
    A a(B(22));
    a.print();
}

I'm well aware that using A a(); declares a function, not a object. But I didn't expect that A a(B(some_val)) will do the same, and in my opinion this is what's happening here.

Do you have ideas why this is happening?


Edit: Thank you for all the answers, looks like I need to research more on most vexing parse idea.

BTW it turns out that compiling my code using clang provides more useful error message plus a solution:

$ clang test.cpp 
test.cpp:21:8: warning: parentheses were disambiguated as a function declaration [-Wvexing-parse]
    A a(B(someTimeVar));
       ^~~~~~~~~~~~~~~~
test.cpp:21:9: note: add a pair of parentheses to declare a variable
    A a(B(someTimeVar));
        ^
        (             )
test.cpp:22:6: error: member reference base type 'A (B)' is not a structure or union
    a.print();
    ~^~~~~~
1 warning and 1 error generated.
3

There are 3 answers

2
Captain Giraffe On BEST ANSWER

This problem has its own tag here on stackoverflow. most-vexing-parse

Wikipedia har a clear description of the problem and its solution. https://en.wikipedia.org/wiki/Most_vexing_parse.

The line

TimeKeeper time_keeper(Timer());

could be disambiguated either as

  • a variable definition for variable time_keeper of class TimeKeeper, passed an anonymous instance of class Timer or
  • a function declaration for a function time_keeper which returns an object of type TimeKeeper and has a single (unnamed) parameter which is a function returning type Timer (and taking no input). (See Function object#In C and C++)

Most programmers expect the first, but the C++ standard requires it to be interpreted as the second.

The solution is to add parenthesis to the argument like:

     A a( (B(22)) );

or as others have noted use universal initialization like

     A a { B{22} }; 
0
Anton Savin On

A a(B(someTimeVar)) is interpreted as A a(B someTimeVar), so a is a function taking parameter of type B and returning A.

This is one of the reasons the uniform initialization was added to C++11:

A a{B{someTimeVar}};
0
Marcel Krüger On

A a(B(someTimeVar)); declares a function with the return type A and one argument of type B named someTimeVar. It's the same as A a(B someTimeVar);

It works in A a(B(22)); because 22 is not a valid identifier, so the functio declaration would be invalid.

If your codebase uses C++11 (or newer), you can use uniform initialization with curly braces: A a(B{someTimeVar});