views:

417

answers:

2

The code below does not compile in Visual C++ 2005.

class SomeClass {
public: boost::function<void()> func;
        SomeClass(boost::function<void()> &func): func(func) { }
};

void someFunc() {
    std::cout << "someFunc" << std::endl;
}

int main() {
    SomeClass sc(boost::function<void()>(&someFunc));
    sc.func(); // error C2228: left of '.func' must have class/struct/union
    return 0;
}

If I put parentheses around the argument to the SomeClass constructor or constructs the boost::function object outside the argument list it compiles fine.

    SomeClass sc((boost::function<void()>(&someFunc)));
    // or
    boost::function<void()> f(&someFunc);
    SomeClass sc(f);

What is the problem with the previous code?

A: 

This is known as "C++'s Most Vexing Parse" (from a book by Scott Meyers called Effective STL).

As answered above, the compiler prefers to interpret the problematic line as a function declaration.

Assaf Lavie
+1  A: 

It's a function declaration for a function taking a reference to a boost:function <void()> and returning a SomeClass. You can memorize the following rule, which turns out to apply to many other such disambiguation cases. You can find descriptions of these cases in section 8.2 of the C++ Standard.

Any construct that could possibly be a declaration will be taken as a declaration

That means, the following will be taken as a parameter declaration, with superfluous parentheses

boost::function<void()>(&someFunc)

If you remove the parentheses, this will become clear

boost::function<void()> &someFunc

And thus, the whole declaration will not anymore declare an object, but a function

SomeClass sc(boost::function<void()> &someFunc);

To fix it, use the cast-notation

SomeClass sc((boost::function<void()>)&someFunc);

Or put parentheses around the whole expression, like you did.

Here is the Standard in all its glory from 8.2:

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 construct that could possibly be a declaration a declaration. [Note: a declaration can be explicitly disambiguated by a nonfunction-style cast, by a = to indicate initialization or by removing the redundant parentheses around the parameter name. ]

Note that for controlling precedence, you are allowed to introduce parentheses just about anywhere, like in the following

int (((((((a))))))) = 3;
int (*(pa)) = &a;
Johannes Schaub - litb