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?
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;