views:

207

answers:

3

Observation: the codes pasted below were tested only with GCC 4.4.1, and I'm only interested in them working with GCC.

Hello,

It wasn't for just a few times that I stumbled into an object construction statement that I didn't understand, and it was only today that I noticed what ambiguity was being introduced by it. I'll explain how to reproduce it and would like to know if there's a way to fix it (C++0x allowed). Here it goes.

Suppose there is a class whose constructor takes only one argument, and this one argument's type is another class with a default constructor. E.g.:

struct ArgType {};

class Class
{
public:
    Class(ArgType arg);
};

If I try to construct an object of type Class on the stack, I get an ambiguity:

Class c(ArgType()); // is this an object construction or a forward declaration
                    // of a function "c" returning `Class` and taking a pointer
                    // to a function returning `ArgType` and taking no arguments
                    // as argument? (oh yeh, loli haets awkward syntax in teh
                    // saucecode)

I say it's an object construction, but the compiler insists it's a forward declaration inside the function body. For you who still doesn't get it, here is a fully working example:

#include <iostream>

struct ArgType {};
struct Class {};

ArgType func()
{
    std::cout << "func()\n";
    return ArgType();
}

int main()
{
    Class c(ArgType());

    c(func); // prints "func()\n"
}

Class c(ArgType funcPtr()) // Class c(ArgType (*funcPtr)()) also works
{
    funcPtr();
    return Class();
}

So well, enough examples. Anyone can help me get around this without making anything too anti-idiomatic (I'm a library developer, and people like idiomatic libraries)?

-- edit

Never mind. This is a dupe of http://stackoverflow.com/questions/1424510/most-vexing-parse-why-doesnt-a-a-work.

Thanks, sbi.

+3  A: 

This is known as "C++'s most vexing parse". See here, here, and here.

sbi
+1  A: 

Based on the "C++0x allowed", the right answer is (probably) to change the definition to:

Class c(ArgType {});

Simple, straightforward and puts the burden entirely on the user of the library, not the author!

Edit: Yes, the ctor is invoked -- C++ 0x adds List-Initialization as an unambiguous way to delimit initializer lists. It can't be mis-parsed like in your sample, but otherwise the meaning is roughly the same as if you used parentheses. See N3000, the third bullet point under §8.5.4/3. You can write a ctor to receive an initializer list as a single argument, or the items in the initializer list can be matched up with the ctor arguments individually.

Jerry Coffin
Wait.. but that wouldn't call the constructor, would it?
n2liquid
Actually that doesn't seem acceptable to GCC 4.4.1. Other combinations are however: `Class c{ArgType {}};` or `Class c{ArgType ()};` And adding a non-C++0x version of invoking the constructor: `Class c((ArgType ()));`
UncleBens
+1  A: 

Let's simplify a little.

int f1();

What's that? The compiler (and I) say it's a forward declaration for a function returning an integer.

How about this?

int f2(double );

The compiler (and I) say it's a forward declaration for a function taking a double argument and returning an int.

So have you tried this:

ClassType c = ClassType(ArgType());

Check out the c++ faq lite on constructors for explanations and examples

Liz Albin