简体   繁体   English

类层次结构中完美转发构造函数和复制构造函数之间的冲突

[英]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. 所以我会尝试澄清我的意图和背景。

  • 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) 这里的原因是在使用临时对象初始化成员时保存一些对象副本(正如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 (ie regarding the example having two constructors with parameters 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM