[英]Conflict between perfect forwarding constructor and copy constructor in class hierarchy
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> &' 错误3错误C2664:'std :: basic_string <_Elem,_Traits,_Alloc> :: basic_string(const std :: basic_string <_Elem,_Traits,_Alloc>&)':无法将参数1从'const Test'转换为'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 . 例如,参见Scott Meyers:C ++ 11中的复制构造函数 。 In other implementations without a class hierarchy I could disable the perfect forwarding constructor from being a copy constructor through SFINAE. 在没有类层次结构的其他实现中,我可以通过SFINAE禁用完美转发构造函数作为复制构造函数。 See for example Martinho Fernandes: Some pitfalls with forwarding constructors . 例如,参见Martinho Fernandes:转发构造函数的一些缺陷 。 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). 就像这个问题的答案和评论所建议的那样:这里可能的解决方案是引入显式强制转换或使用单独的非模板化构造函数(即关于具有两个构造const string&
的示例,其中参数为const string&
和string&&
)。 Try changing Test(const Test& other) : TestBase(other) {}
to Test(const Test& other) : TestBase(static_cast<TestBase const&>(other)) {}
尝试更改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. 第二个Test
构造函数调用TestBase,有两种可能性。 One of them takes anything, the other takes a TestBase. 其中一个需要任何东西,另一个需要一个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. 通过显式地转换为TestBase const&,我们应该能够得到正确的匹配。
Another possibility might involve how Test is constructed -- maybe what you passed in matched the template constructor to Test instead? 另一种可能性可能涉及如何构造Test - 也许你传递的是将模板构造函数与Test相匹配? We can test this other possibility by removing the template constructor from Test and seeing if the error goes away. 我们可以通过从Test中删除模板构造函数并查看错误是否消失来测试另一种可能性。
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? 如果是这种情况,为什么你链接的技术(当推断的类型与Test匹配时禁用Test模板构造函数)不起作用?
Let's have a closer look at the error message. 让我们仔细看看错误消息。
std::basic_string<...>::basic_string(const std::basic_string<...> &) :
That means it applies to a copy constructor of std::string
这意味着它适用于std::string
的复制构造函数
cannot convert parameter 1 from 'const Test' to 'const std::basic_string<..> &
Indeed, there's no way to convert from Test
to std::string
. 实际上,没有办法从Test
转换为std::string
。 However, Test
has a string member, namely, std::string s;
但是, Test
有一个字符串成员,即std::string s;
. 。
Conclusion: it looks like you forgot to add .s
at that place. 结论:看起来你忘了在那个地方添加.s
。 Probably, it is in s(std::forward<T>(t))
. 可能是s(std::forward<T>(t))
。
Another possible reason is that the 1st overload of the constructor was picked instead of the 2nd for copy-constructing an instance of Test
. 另一个可能的原因是构造函数的第一个重载被选中而不是第二个用于复制构造一个Test
实例。
The following should work and it uses no explicit casts: 以下应该工作,它不使用显式转换:
struct Test : public TestBase {
private:
static TestBase const& toBase(const Test& o) { return o; }
public:
template <typename T>
explicit Test(T&& t) : TestBase(std::forward<T>(t)) {}
Test(const Test& other) : TestBase(toBase(other)) {}
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.