C++隐式复制和移动构造函数的原理是什么?

12

我对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类型进行转换。

然后我想让work1common_work继承,出于EBO和其他原因。所以新的work1类看起来像:

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

作为一个work_like类,work1在使用转发构造函数时得到了更好的匹配,因为common_work的复制/移动构造函数需要从派生类向基类进行static_cast

注意:

  • Scott Meyers提供了一个类似的例子,其中复制构造函数触发转发构造函数,因为复制构造函数需要常量添加,而转发构造函数不需要。但我认为,这个问题是由错误的类设计引起的,而这里的问题是在隐式复制/移动期间传递给基类的参数不是精确匹配。
  • 我不能编写通用的转发构造函数 / 赋值,并删除隐式函数,因为删除的函数也参与重载决议,如果匹配恰好,则会导致错误。
  • 目前我拥有的解决方案是使 common_work 成为 CRTP ,即派生类类型作为模板参数传递,并在转发构造函数中将其过滤为 enable_if<and_<work_like<T>,not_<is_same<T,Derived> > > >。否则,我必须手动编写 work1 的复制/移动构造函数/赋值,并明确地将其转换为基类,这很容易出错,存在维护危险。

也许您可以通过加强SFINAE约束条件来解决此问题,使其拒绝从“common_work”派生的“T”。 - Andy Prowl
这是我目前所做的。请查看“注意”部分的第三点。但是,它不应该是is_same<T,Derived>,而应该是is_same<remove_ref<T>,Derived> - abir
好的,抱歉。我没有看到最后一条注释 :) - Andy Prowl
1
我无法在其他编译器上进行测试,但是使用G++ 4.7.2:test。如果您没有定义(“用户提供”)复制构造函数,则编译器似乎会隐式地执行“转换为基类”...您能否在您的平台上测试该示例? - gx_
1个回答

1
这个问题在几年前的 MSVC++ bugstracker 页面上进行了讨论(所以如果现在还没有修复,那么在 MSVC++ 上这是一个已知问题)。标准规定:

  • ... 基类或成员通过 x 的相应基类或成员进行直接初始化。

当我读到这个错误报告时,我测试了各种编译器,它们都会 "神奇地转换"。标准似乎对细节(以及值类别和 c/v 限定符)保持沉默,并且只是说 "使用 x 的相应基础... ",在我看来,如果你把它理解为 "使用不是 x,而是 x 的相应基础... ",那就更有意义了(我甚至会说只有这样才有意义)。


请注意,MSVC尚未实现显式默认的特殊成员函数(尽管他们表示将在2013 RTM中很快实现它们)。 - Jesse Good

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接