Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Construction of temporary in function call is interpreted as declaration

Lately I ran into a problem which somehow (but only somehow) makes sense to me. It is based on interpreting the construction of a temporary as declaration of the single (!) constructor argument. Please have a look at the minimal example below.

#include <iostream>

class Foo0{
public:
  Foo0(int a){};
  void doStuff() {std::cout<<"maap"<<std::endl;};
};

class Foo1{
public:
  Foo1(int a){};
  void doStuff() {std::cout<<"maap"<<std::endl;};
};

class Foo2{
public:
  Foo2(int a){};
  void doStuff() {std::cout<<"maap"<<std::endl;};
};

class Bar{
public:
  Bar(Foo0 foo0, Foo1 foo1, Foo2 foo2){};
};

int main () {
  int x = 1;

  Bar bar0(Foo0(x), Foo1(x), Foo2(x)); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’; conflicting declaration ‘Foo2 x’ previous declaration as ‘Foo0 x’
  Bar bar1(Foo0{x}, Foo1(x), Foo2(x)); // Works WTF
  Bar bar2(Foo0(x), Foo1{x}, Foo2(x)); // Works WTF
  Bar bar3(Foo0(x), Foo1(x), Foo2{x}); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’
  Bar bar4(Foo0{x}, Foo1{x}, Foo2{x}); // Works totally makes sens to me

  x.doStuff(); //Dose not work. This makes sens to me. But in the context its curious though.
}

I already read that expressions like:

Foo(a);

Are interpreted (if there is a standard constructor) as declaration of a. This makes sense and it is totally fine, since you can just use the {}-brackets to make the construction explicit. But what I do not understand is:

  1. Why is there a problem with the construction of bar0? All Foos do not have a standard constructor. So it does not make sense to interpret something like Foo0(x) as declaration of x.

  2. Why does the construction of bar1 and bar2 work? It is obvious to me that the construction of bar4 works, since I use the {}-brackets for all temporary Foos, thus I am explicit about what I want.

  3. If it is only necessary to use the {}-brackets with only one of the Foos to solve the problem... why does the construction of bar3 fail?

  4. Furthermore, x is declared before any Bar is constructed. Why does the compiler not complain about that?

The last question is related to my last line of example code. Long story short: What does the compiler think that I want him to do and where do I miss the appearance of shadowing?

PS: If it is of interest -- I use the gcc-4.9.2.
PPS: I tried the same with bar's constructor taking three Foo0s as arguments. Same story here. But the error says nothing about conflicting declaration but about redefinition of x.

like image 788
sedriel Avatar asked Jan 19 '15 22:01

sedriel


1 Answers

The rule is that if a declaration has the syntax of a function declaration then it is one; otherwise it is a variable declaration. Surprising instances of this are sometimes called most-vexing-parse.

Bar bar0(Foo0(x), Foo1(x), Foo2(x)); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’; conflicting declaration ‘Foo2 x’ previous declaration as ‘Foo0 x’

This is a function declaration: bar0 is the name, Bar is the return type, and the parameter types are Foo0, Foo1 and Foo2. The parameter names are all x which is illegal - the names of function parameters must be different. If you change x x x to x y z the error goes away).

Bar bar1(Foo0{x}, Foo1(x), Foo2(x)); // Works WTF
Bar bar2(Foo0(x), Foo1{x}, Foo2(x)); // Works WTF
Bar bar4(Foo0{x}, Foo1{x}, Foo2{x}); // Works totally makes sens to me

These lines and create objects bar1, bar2, and bar4 of type Bar. They cannot be parsed as function declarations because the { } notation is not valid syntax in a function declaration.

Therefore, Foo0{x} etc. are expressions which provide the arguments to Bar's constructor. Foo0{x} and Foo0(x) are equivalent ways of declaring a temporary of type Foo0 with initializer x.

Bar bar3(Foo0(x), Foo1(x), Foo2{x}); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’

I think this is a compiler bug; the part Foo2{x} means that this line cannot be a function declaration; and it looks like a valid declaration of a variable bar3.

x.doStuff(); //Dose not work. This makes sens to me. But in the context its curious

x is an int ; it does not have any methods.

like image 194
M.M Avatar answered Nov 05 '22 08:11

M.M