4

The following program, compiled with g++ 4.6, yields the error

request for member ‘y’ in ‘a2’, which is of non-class type ‘A<B>(B)’

at its last line:

#include <iostream>

template <class T> class A
{
public:
  T y;
  A(T x):y(x){}
};

class B
{
public:
  int u;
  B(int v):u(v){}
};

int main()
{
  int v = 10;
  B b1(v);

  //works
  A<B> a1(b1);

  //does not work (the error is when a2 is used)
  A<B> a2(B(v));

  //works
  //A<B> a2((B(v)));

  std::cout << a1.y.u << " " << a2.y.u << std::endl;    
}

As can be seen from the working variant included in the code, adding parentheses around the arguments of the constructor of A solves the problem.

I have seen some related errors caused by a the interpretation of a constructor invocation as a function declaration, like when creating an object with no argument to its constructor, but with braces:

myclass myobj();

but it seems to me that

A<B> a2(B(v));

cannot be interpreted as a function declaration.

Someone can explain to me what is happening?

fredoverflow
  • 256,549
  • 94
  • 388
  • 662

4 Answers4

8

It's a case of most vexing parse where the compiler interprets A<B> a2(B(v)) as the declaration of a function. Such that:

A<B> is the return type
a2 is the function name
B is the type of the argument
v is the argument name

So, when you are doing

std::cout << a1.y.u << " " << a2.y.u << std::endl;

The compiler does not think of a2.y.u as a class, that's why you are getting the non-class type error.

Also, since double parenthesis aren't allowed in a function declaration, the version A<B> a2((B(v))); works because the compiler doesn't interprets it as a function declaration anymore, but as a variable declaration.

Mesop
  • 5,187
  • 4
  • 25
  • 42
  • A more interesting question is how come that `v` being an integer variable, the expression `A a2(B(v));` can be parsed as a function declaration... (which is actually the question asked) --It seems clear from the comments at the end that the user already knew this was a case of the most-vexing-parse but wanted to know *how* the compiler got there. – David Rodríguez - dribeas Jun 21 '12 at 13:16
  • @DavidRodríguez-dribeas You are right, I added how the compiler interpreted the line itself. – Mesop Jun 21 '12 at 13:25
  • Wow, I hadn't realized the most vexing parse could get this bad. `int foo(int (bar)) { return 0; }` Why are parentheses allowed around the argument name? I had thought the most vexing parse only happened when the initializer could be parsed as a function type. Now I see that because parameter declarations can have parenthesis around the parameter name it's worse than that. I guess I'm surprised that they didn't go ahead and make 'disambiguating' parentheses legal for function declarations as well so we couldn't disambiguate: `int foo((int (bar))) { return 0; }` – bames53 Jun 21 '12 at 13:46
  • Apparently the parens are allowed because they're allowed in normal variable declarations: `int main() { int (bar) = 0; }` Why? – bames53 Jun 21 '12 at 13:50
  • @bames53 The same way they are needed in int (*f)() to declare a function pointer, I think they need to be uniformelly allowed in other situations. – oblitum Jun 21 '12 at 13:57
  • I think it's worth to mention the golden rule here: http://www.antlr.org/wiki/display/CS652/How+To+Read+C+Declarations – oblitum Jun 21 '12 at 14:06
  • One more thing to note: you can use C++11's uniform initialization syntax to get around this vexing parse: A a2{B(v)}; – bstamour Jun 21 '12 at 15:34
  • Wow, I didn't know that it was allowed to put parentheses around function argument names. Thanks for the answer! – user1001031 Jun 21 '12 at 18:33
2

I think you're getting bit by the "most vexing parse", meaning that A<B> a2(B(v)); is getting parsed as function declaration instead of a variable declaration.

Fred Larson
  • 60,987
  • 18
  • 112
  • 174
2

As seen in the following code sample:

int a (int(v)) {
    return v;
}

int main() {
    std::cout << a(5); //prints 5
}

That line is indeed a declaration. In this example, the parameter is of type int and named v. Relating that to your code, the parameter is of type B and is named v. That's why you get the similar behaviour when you use double parentheses: because it's the same thing!

chris
  • 60,560
  • 13
  • 143
  • 205
2

It is a function declaration:

A<B> a2(B(v));
//is same as:
A<B> a2(B v);

//consider:
int foo(int v);
int foo(int (v));
Agent_L
  • 4,960
  • 28
  • 30