views:

69

answers:

3

Hi there,

I have the following C++ code in Visual Studio 2005...

class Base {};
class Derived : public Base {};

class Other {
public:
 Other(const Base& obj) {}
 void test() {}
};

int _tmain(int argc, _TCHAR* argv[])
{
 Other other(Derived());
 other.test();
 return 0;
}

... Compilation fails and gives:

test.cpp(19) : error C2228: left of '.test' must have class/struct/union

I've determined through a few tests that this happens because the declaration of the "other" variable is interpreted as a function declaration (returning a Other and taking a Derived parameter), instead of an instance of Other using the single-argument constructor. (VS6 finds the constructor and compiles it fine, but it's not good at standard C++ so I don't trust it compared to VS2005)

If I do...

Other other(static_cast<Base&>(Derived()));

... or use copy-initialization, it works fine. But it doesn't seem to see that the Derived() instance is derived from Base on its own, or it prioritizes function declaration instead of trying for polymorphism on the constructor parameter.

My question is: is this standard C++ behavior, or is this VS2005-specific behavior? Should...

Other other(Derived());

... declare a local instance in standard C++, or should it declare a function?

+3  A: 

Yes, this is standard behavior. See this C++ FAQ-lite entry.

According to the standard, this:

Other other(Derived());

gets interpreted as a function declaration, of a function that returns Other and takes as a parameter another function that returns Derived and has no parameters. To fix this, you can use:

Other other = Other(Derived());
interjay
Wow, thank you. Ironically I had already read that link in the past, but must have forgotten (probably since the given reason behind it - the parameter interpretation - is pretty darn obscure). I assumed it had to do with polymorphism because the static_cast<Base>(...) worked too.
DustOff
+1  A: 

I tried your example in VS2008, and GCC and it happens there too.

What it looks like, is that that syntax is actually declaring other as a function pointer with this declaration:

Other other(Derived (*)(void))

The correct behavior should be to use initialization:

Other other = Derived();
Ramon Zarazua
+2  A: 

You mention "polymorphic parameter" in your question's title, while in fact all this has absolutely nothing to do with any polymorphic parameters. In C++ the problematic declaration declares a function regardless of whether the "argument" you supply is polymorphic or not.

As usual, you can use an extra pair of () to work around the issue

Other other((Derived())); // now it is an object, not a function

You can also use any other method in order to turn the Derived() part into an expression, like

Other other((const Derived&) Derived()); 

or

Other other(((void) 0, Derived())); 
AndreyT
DustOff
@DustOff: `( (void) 0, Derived() )` is an expression that contains a comma operator in the middle. The result of the comma operator is its last subexpression - `Derived()` - which is what we need. The first subexpression - `(void) 0` - is simply ignored. Just a plain `0` (or `1` or `42`) will work as well, but the compiler will issue warnings about expression that "has no effect". A cast to `(void)` supresses the warning. The value of this weird method is purely academic, of course :)
AndreyT