std :: move()在没有move-ctor的情况下调用copy-ctor。为什么以及如何预防呢?

时间:2013-08-16 00:49:32

标签: c++ c++11 move-semantics

我想知道是否有一个安全编程练习可以提醒编码人员发生这种微妙的行为,或者更好的是,首先避免它。

struct A的用户可能没有意识到没有移动构造函数。在他们试图调用缺席的ctor时,他们既没有得到编译器警告,也没有得到复制ctor被调用的任何运行时指示。

下面的答案解释了发生的转换,但我认为这不是一件好事。如果构造函数缺少 const引用作为参数,则会出现编译时错误,而不仅仅是解析为非const引用版本。那么,为什么在类中没有实现移动语义时,尝试使用移动语义会导致编译时错误?

有没有办法通过一些编译时选项来避免这种行为,或者至少是一种在运行时检测它的方法?

移动后可以assert(source is null) 如果他们正在预料到这个问题,但对于这么多问题都是如此。

示例,时间:

struct A {
    A() {...}
    A(A &a) {...}
    A(A const & A) {...}
};

的构造如下:

A a1;
A a2 = std::move(a1);  //calls const copy (but how would I know?)

这会导致调用复制ctor的const版本。现在两个对象可能有一个指向单个资源的指针,而其中一个可能很快就会调用它的析构函数。

4 个答案:

答案 0 :(得分:4)

因为std::move返回一个rvalue,它可以转换为const ref,这就是为什么复制ctor被静默调用的原因。您可以通过多种方式解决此问题

  1. 简单来说,如果你的班级没有动态分配,只需使用这样的默认监听器 A(A &&) = default;
  2. 摆脱了cctor中的const,这不是一个好主意,但它不会编译而不是

  3. 只需实现自己的移动构造函数,如果没有mctor,为什么要移动到对象


  4. 这稍微偏离主题,但您也可以限定成员函数仅使用像这样的可修改左值。

    int func()&;

    如果您正在处理遗留代码,请在move constructable

    进行编译时检查

答案 1 :(得分:3)

没有一种安全的编程习惯可以提醒这样的事情,因为在std::move之后复制是非常常见和正常的。添加警告会导致<algorithm>中的所有内容开始触发警告,我们需要一个全新的功能,我们使用的功能完全就像std::move一样现在。如果没有移动构造函数,或者移动,那么很多算法都依赖于复制,而就是std::move用于的算法。充其量你应该争论是否存在std::force_move

其次,这完全没必要。获取您展示的代码的这个修改版本。

void legacy_api(A a1) {
   A a2 = std::move(a1);
   ...

你说这是一个问题,它巧妙地使用了昂贵的副本,而不是一个微妙的举动。我不同意,需要什么是对象的新实例,可能会破坏之前的实例。如果代码需要另一个实例,那么它需要另一个实例。它是否破坏了以前应该是完全无关紧要的。如果代码在不需要其他实例的情况下移动,那么延迟API显然是 pooly written ,并且前面提到的警告对此没有帮助。我想不出函数会要求移动但没有复制的任何理由,这样的事情没有任何目的。

最后,“现在两个对象可能有一个指向单个资源的指针,而其中一个可能很快就会调用它的析构函数。”不,如果发生这种情况那么这意味着A的复制构造函数有一个bug,句点。修复代码中的错误,问题就消失了。像魔术一样!

答案 2 :(得分:1)

因为std :: move返回对rvalue的引用(如在A&amp;&amp;中),它不能转换为对左值(A&amp;)的引用,但可以转换为对左值的const引用(const A&amp;)。 )。

int x = 5.这里5是一个rvalue,永远不能绑定到左值,在你的例子中也是如此,因为你使用了std :: move(a1)

答案 3 :(得分:1)

如果您是要移动的类的所有者,但该类没有移动构造函数,因为您不想移动它,您可以写:

class A
{
public:
   // Other constuctors

   A(A&&) = delete;
};

因此,当您尝试移动A时,如果我没有错,则会抛出编译器错误。如果你想要总是移动对象,你当然应该编写自己的移动构造函数,或者启用默认的移动构造函数:

 A(A&&) = default;

如果您不是该类的所有者,我认为没有直接的方法可以避免调用复制构造函数。或许强迫它的成语可能如下:

template<bool b, typename T>
using enabling = typename std::enable_if<b, T>::type;

template<typename T>
constexpr bool movable()
{
   return std::is_move_constructible<T>::value;
}

template<typename T>
enabling<movable<T>(), T&&> movify(T&& t)
{
    return std::move(t);
}

A a1;
A a2 = movify(a1);

那应该有用。我没有测试过,但是我知道你已经抓住了这个想法。