c++隐式复制和移动构造函数背后的基本原理

rationale behind c++ implicit copy and move constructor?

本文关键字:背后 构造函数 复制 移动 c++      更新时间:2023-10-16

我对c++隐式复制构造函数的理解类似于

T(T const& x) : 
    base1(x), base2(x) ... , 
    var1(x.var1), var2(x.var2)...
{}

移动构造函数,复制&移动分配也遵循类似的模式。

为什么它的定义不类似于以下内容

T(T const& x) : 
    base1(static_cast<base1 const&>(x)),
    base2(static_cast<base2 const&>(x)) ... , 
    var1(x.var1), var2(x.var2)...
{}

示例

我有一个类,它有隐式的复制/移动构造函数/赋值运算符,以及一些转换构造函数。我把工作委托给了某个实现类。

class common_work //common implementation of many work like classes
{
   common_work(common_work const&) = default;
   common_work(common_work&&) = default;// ... implicit constructors work for me.
   //a forwarding constructor which can take many work like objects
   template<class T, enable_if<work_like<T> > >
   common_work(T&& x) { ... }
};
class work1 //one of the implementation
{
   work1(work1 const& ) = default;
   work1(work1&& ) = default; ...
   common_work impl_;
};

这很好,因为work1的复制/移动构造函数正在为common_work调用复制/移动构造器,而转发构造函数被从另一种work转换而来的其他构造函数使用[代码中未显示]。

然后出于EBO等原因,我想从common_work继承work1。所以新的work1类看起来像

class work1 : private common_work
{
   work1(work1 const& ) = default;
   work1(work1&& ) = default; ...
};

但是,由于work1work_like类,转发构造函数突然变得更好匹配,因为common_work的复制/移动构造函数需要从派生到基的static_cast

注意:

  • Scott Meyers给出了一个类似的例子,其中复制构造触发转发构造函数,因为复制构造函数需要常量添加,而转发构造函数不需要。但我认为,这个问题是由于错误的类设计引起的,而这里的问题是由于隐式复制/移动过程中传递给基类的参数不完全匹配引起的
  • 我不能写一个通用的转发构造函数/赋值,也不能删除隐式的,因为被删除的函数也会参与重载解决,如果完全匹配会导致错误
  • 目前我的解决方案是将common_work作为CRTP,即作为模板参数传递的派生类类型,并在转发构造函数中将其过滤为enable_if<and_<work_like<T>,not_<is_same<T,Derived> > > >。否则,我必须手动将work1static_cast的复制/移动构造函数/赋值显式地写入基类,这是有缺陷的、容易出错的,并且有维护风险

这个问题几年前就在MSVC++错误生成器页面上讨论过(因此,如果它还没有修复,那么它就是MSVC++上的已知问题)。标准称

  • 。。。基或成员直接用x的相应基或成员初始化

当我读到错误报告时,我确实测试了各种编译器,所有这些编译器都"神奇地抛出"了。标准似乎对细节保持沉默(也关于值类别和c/v限定符),只是说"有相应的基数……x",如果你把IMO的意思理解为"不是x,而是x的对应基…",而不是你把完整的对象传给它(我甚至会说它只能这样有意义),那么IMO就更有意义了。