【问题标题】:Is there a way to make a moved object "invalid"?有没有办法使移动的对象“无效”?
【发布时间】:2015-10-11 12:02:11
【问题描述】:

我有一些代码可以将一个对象移动到另一个对象中。我将不再需要上层的原始移动对象。因此,我认为移动是正确的选择。

但是,考虑到安全性,我想知道是否有办法使移动的对象无效,从而防止有人访问它时出现未定义的行为。

Here 是一个很好的例子:

// move example
#include <utility>      // std::move
#include <vector>       // std::vector
#include <string>       // std::string

int main () {
  std::string foo = "foo-string";
  std::string bar = "bar-string";
  std::vector<std::string> myvector;

  myvector.push_back (foo);                    // copies
  myvector.push_back (std::move(bar));         // moves

  return 0;
}

描述说:

第一次调用myvector.push_back 会将foo 的值复制到 向量(foo 保留调用前的值)。第二次调用 将bar 的值移动到向量中。这会转移其内容 进入向量(而bar 失去了它的价值,现在处于有效但 未指定的状态)。

有没有办法使bar 无效,这样访问它会导致编译器错误?比如:

myvector.push_back (std::move(bar));         // moves
invalidate(bar); //something like bar.end() will then result in a compiler error

编辑:如果没有这样的事情,为什么

【问题讨论】:

    标签: c++11 move move-semantics


    【解决方案1】:

    访问移动的对象不是未定义的行为。被移动的对象仍然是一个有效的对象,程序很可能希望继续使用该对象。例如,

    template< typename T >
    void swap_by_move(T &a, T &b)
    {
        using std::move;
        T c = move(b);
        b = move(a);
        a = move(c);
    }
    

    【讨论】:

    • 是的,当然,如果您仍然想使用初始化的内存,那也没关系。但是,问题涉及我不想再使用它的情况,事实上我想让它完全无效。
    【解决方案2】:

    更大的答案是因为移动或不移动是在运行时做出的决定,而给出编译时错误是在编译时做出的决定。

    foo(bar); // foo might move or not
    bar.baz(); // compile time error or not?
    

    这行不通。您可以在编译时分析中进行近似计算,但是对于开发人员来说,要得到错误或做出任何有用的东西来保持有效的程序或开发人员必须对调用的函数进行烦人且脆弱的注释,以保证不会移动参数。

    换一种说法,如果您使用包含值 42 的整数变量,或者如果您使用包含空指针值的指针,您将询问编译时错误。您可能成功地使用 clang 分析 API 实现了一个近似的构建时代码约定检查器,但是,如果您不能证明 std::move 直到给定的时候才被调用,则处理 C++ AST 的 CFG 并出错变量的使用。

    【讨论】:

      【解决方案3】:

      移动语义就是这样工作的,所以你可以得到一个处于任何它是正确状态的对象。正确状态意味着所有字段都具有正确的值,并且所有内部不变量仍然是好的。这样做是因为在移动之后您实际上并不关心移动对象的内容,但是资源管理、分配和析构函数之类的东西应该可以正常工作。 所有 STL 类(以及所有使用默认移动构造函数/赋值的类)都只是将其内容与新的交换,因此两种状态都是正确的,并且非常容易实现、快速且足够方便。

      您可以定义具有 isValid 字段的类,该字段通常为 true,并且在移动时(即在移动构造函数/移动赋值中)将其设置为 false。那么您的对象将具有正确状态我无效。只是不要忘记在需要的地方检查它(析构函数、赋值等)。

      isValid 字段可以是具有空值的任一指针。重点是:您知道,该对象在移动后处于可预测状态,而不仅仅是内存中的随机字节。

      编辑:String 的示例:

      class String {
      public:
          string data;
      private:
          bool m_isValid;
      
      public:
          String(string const& b): data(b.data), isValid(true) {}
      
          String(String &&b): data(move(b.data)) {
              b.m_isValid = false;
          }
      
          String const& operator =(String &&b) {
              data = move(b.data);
              b.m_isValid = false;
              return &this;
          }
      
          bool isValid() {
              return m_isValid;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-04
        • 1970-01-01
        • 1970-01-01
        • 2016-03-31
        • 2019-09-27
        • 1970-01-01
        相关资源
        最近更新 更多