tags:

views:

836

answers:

3

Have a look here: In the following code, what would be the type of b?

struct A {
    A (int i) {}
};

struct B {
    B (A a) {}
};

int main () {
    int i = 1;
    B b(A(i)); // what would be the type of b
    return 0;
}

I'll appreciate it if anybody could explain to me thoroughly why would such syntax exist :)

Thanks.

+4  A: 

It is a local function declaration according to C++ Standard 8.2/1. You could use implicit form of constructor to avoid this or the following:

B b(A(i)); // is equal to B b( A i );

// ---

// to declare variable of type B write:
B b = A(i);
// explicit form if you want:
B b( static_cast<A>(A(i)) );
// or
B b( (A)i );

C++ Standard 8.2/1:

The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 6.8 can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in 6.8, the resolution is to consider any con- struct that could possibly be a declaration a declaration.

Kirill V. Lyadvinsky
+5  A: 

One of C's warts (and C++ inherits it (and makes it worse)) is that there is no special syntax for introducing a declaration. This means declarations often look like executable code. Another example:

A * a;

Is this multiplying A by a, or is it declaring something? In order to make sense of this line you have to know that A is the name of a type.

The basic rule in C++ is that if something can be parsed as a declaration, it is. In this instance it leads to a strange and surprising result. Function declarations look a lot like function calls, and in particular the ( after the A can be thought of in a couple of ways.

You can get around this in this example with extra parenthesis that remove the compiler's ability to parse the code as a declaration.

B b((A(i)));

In C this isn't ambiguous because there is no function style of constructor call because there are no constructors. A is either the name of a type, or it's the name of a function. It can't be both.

Omnifarious
I believe you could also do: B b = B(A(i));
Bill
Yes, that would also work, is easier to understand and less ugly. :-)
Omnifarious
... but means a different thing - `B b = B(...)` requires `B` to have a copy constructor (even though the compiler can optimize the call to it away, it is still required to check), while `B b(...)` does not require it.
Pavel Minaev
+3  A: 
B b(A(i));

is equivalent to

B b(A i);

- the parenthesis around the argument name are optional -, which is equivalent to

B b(A);

- the parameter name is optional in function declarations. Hence it is a function declaration.

Typically you run into it with

X x();

- not default constructor as expected -, but there are more complicated cases when using temporaries all the way, e.g

vector<int> v(istream_iterator<int>(cin), istream_iterator<int>());
UncleBens
Unexpectedly, it turned into a good argument against `using namespace` :)
Pavel Minaev
Unfortunately it goes deeper than that. If cin had been std::cin then GCC determines that this line indeed constructs a vector object, whereas VC++ (2005) complains that std::cin cannot appear as parameter name.
UncleBens
@UncleBens, Unfortunately, GCC is wrong with that and VC++ is correct. See my defect report to clang: http://llvm.org/bugs/show_bug.cgi?id=4594
Johannes Schaub - litb