I'd like to pass a temporary object(std::string for example) to the constructor of my object:
class MyClass{
public:
MyClass(string a):
a(a)
{
}
string a;
};
int main(int argc, char *argv[]){
MyClass a(string());
cout<<a.a<<endl;
return 0;
}
But I receive this error:
main.cpp: In function ‘int main(int, char**)’:
main.cpp:28:11: error: request for member ‘a’ in ‘a’, which is of non-class type ‘MyClass(std::string (*)()) {aka MyClass(std::basic_string<char> (*)())}’
Everything works ok if I pass anything to the constructor of temporary object(for example string("")). Why?
This is an instance of what has been dubbed C++'s most vexing parse. The compiler interprets
MyClass a(string());
as the prototype of a function named a
returning a MyClass
and taking an unnamed string
as a parameter.
You can disambiguate it by putting parenthesis around the call to string
s constructor:
MyClass a((string()));
Or, as long as MyClass
has an accessible copy constructor:
MyClass a = string();
Update for C++11
You can also disambiguate with the universal initialisation syntax, as long as your class doesn't have a constructor that takes an initializer_list
:
MyClass a { string() };
As it has already been pointed out by Seth, that is a problem with the language and how the expression is parsed. Basically when the compiler finds the expression MyClass a(string())
it interprets it as the declaration of a function a
that has the signature MyClass (std::string (*)())
(The extra (*)
comes from an implicit conversion from function to pointer to function in arguments).
There are different approaches to overcome that syntax in your particular case:
MyClass a(""); // 1
MyClass b = std::string(); // 2
MyClass c(( std::string() )); // 3
The first approach is not to use the T()
expression to create the rvalue, but rather a constant literal that will produce the same output. The shortcoming of this approach is that in this particular case you can use a literal, but the same solution cannot be applied to other types (i.e. if you had your own type that only had a default constructor)
The second approach is avoiding direct initialization, as the alternative syntax cannot be parsed as a function declaration. The problem with this approach is that while the result in your particular case is the same, it requires the constructor not to be explicit. (I.e. it would fail if your constructor was declared explicit MyClass( std::string const & )
), but it is none
The third approach is adding an extra set of parenthesis around the first argument to the constructor (note that the same problem in parsing would happen with MyClass a(std::string(), std::string())
). The problem with this approach (opinion) is that it is ugly, but it is otherwise the most flexible of the three.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With