I recently encountered a problem while trying to implement a class hierarchy with perfect forwarding constructors. Consider the following example:
struct TestBase {
template<typename T>
explicit TestBase(T&& t) : s(std::forward<T>(t)) {} // Compiler refers to this line in the error message
TestBase(const TestBase& other) : s(other.s) {}
std::string s;
};
struct Test : public TestBase {
template<typename T>
explicit Test(T&& t) : TestBase(std::forward<T>(t)) {}
Test(const Test& other) : TestBase(other) {}
};
When I try to compile the code I get the following error:
Error 3 error C2664: 'std::basic_string<_Elem,_Traits,_Alloc>::basic_string(const std::basic_string<_Elem,_Traits,_Alloc> &)' : cannot convert parameter 1 from 'const Test' to 'const std::basic_string<_Elem,_Traits,_Alloc> &'
My understanding is that the compiler treats the perfect forwarding constructor as a better math than the copy constructor. See for example Scott Meyers: Copying Constructors in C++11 . In other implementations without a class hierarchy I could disable the perfect forwarding constructor from being a copy constructor through SFINAE. See for example Martinho Fernandes: Some pitfalls with forwarding constructors. When I try to apply the mentioned solution to this example I still cannot compile with the same error message.
I think one possible solution would be to avoid the perfect forwarding, take the parameters by value in the constructors and than move from them to the class variables.
So my question is if there are some other solutions to this problem or if perfect forwarding in not possible in such a case?
Update: It turned out that my question is easy to misunderstand. So I will try to clarify my intentions and the context a bit.
- The code is complete like posted in the question. There are no other objects created or functions called. The error appeared while trying to compile the posted example.
- The purpose of having the perfect forwarding constructor is for member initialization and not to have some kind of extra copy constructor. The reason here is to save some object copies when initializing members with temporary objects (as proposed in talks by Scott Meyers)
- Unfortunately as it turned out perfect forwarding constructor can conflict with other overloaded constructors (in this example with the copy constructors).
- Like the answers and comments to this question suggested: Possible solutions here would be to introduce explicit casts or having separate non-templated constructors (i.e. regarding the example having two constructors with parameters
const string&
andstring&&
respectively).