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.
const string&
and string&&
respectively).When we create our own copy constructor, we pass an object by reference and we generally pass it as a const reference. One reason for passing const reference is, we should use const in C++ wherever possible so that objects are not accidentally modified.
Passing by value (rather than by reference) means a copy needs to be made. So passing by value into your copy constructor means you need to make a copy before the copy constructor is invoked, but to make a copy you first need to call the copy constructor. Save this answer.
A copy constructor has as its first parameter a (possibly const or volatile) reference to its own class type. It can have more arguments, but the rest must have default values associated with them.
Try changing Test(const Test& other) : TestBase(other) {}
to Test(const Test& other) : TestBase(static_cast<TestBase const&>(other)) {}
The 2nd Test
constructor is calling TestBase, and there are two possibilities. One of them takes anything, the other takes a TestBase. But you are passing a Test to it -- the "anything" matches better. By explicitly casting to a TestBase const&, we should be able to get the right one to match.
Another possibility might involve how Test is constructed -- maybe what you passed in matched the template constructor to Test instead? We can test this other possibility by removing the template constructor from Test and seeing if the error goes away.
If that is the case, why wouldn't the technique you linked (to disable the Test template constructor when the type deduced matches Test) work?
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