C++ 使用std::move';其他地方

C++ 使用std::move';其他地方,c++,c++11,move-semantics,C++,C++11,Move Semantics,这: void foo(int&r){ 标准::cout 如果我在移动变量后意外使用该变量,是否无法告诉编译器给我一个错误(或警告) 答案是“不,没有办法”(至少就我所知,目前没有可用的编译器提供这样的选项,并且有一个很好的理由——请参见下文) 即使这是可能的,为什么在这种情况下会出现警告,甚至更糟糕的错误呢?首先,从整数开始移动与复制它没有什么不同 其次,对于大多数类型,指定该类型的moved from对象是完全合法的操作;对于基本类型(如int)总是如此,对于std::vector)也是如此

这:

void foo(int&r){
标准::cout
如果我在移动变量后意外使用该变量,是否无法告诉编译器给我一个错误(或警告)

答案是“不,没有办法”(至少就我所知,目前没有可用的编译器提供这样的选项,并且有一个很好的理由——请参见下文)

即使这是可能的,为什么在这种情况下会出现警告,甚至更糟糕的错误呢?首先,从整数开始移动与复制它没有什么不同

其次,对于大多数类型,指定该类型的moved from对象是完全合法的操作;对于基本类型(如
int
)总是如此,对于
std::vector
)也是如此,尽管对于其他类型可能不是这样

通常,指定“移动自”对象是否合法取决于该类型的移动操作的特定post条件以及指定运算符的先决条件(标准库类型的指定运算符在左侧参数上没有先决条件).这是编译器在一般情况下无法检查的

因此,如果您要:

  • 从移动赋值或移动构造函数将移动自对象置于未指定状态的对象中移动(这是
    std::vector
    的情况),然后
  • 在该对象的状态上调用任何具有先决条件的函数(这不是
    std::vector
    赋值的情况)
  • 这肯定是不好的。另一方面,编译器没有办法对程序执行语义分析,并找出是否存在这种情况:

    struct HugeStorage {
      std::vector<double> m_vec;
      HugeStorage(std::vector<double> vec) : m_vec(std::move(vec)) { }
    };
    
    struct SmallStorage {
      std::vector<double> m_vec;
      SmallStorage(std::vector<double> vec) : m_vec(std::move(vec)) { }
    };
    
    std::vector<double> vec_from_data_source() {
      return std::vector<double>(); //only example!!
    }
    
    int main() {
      std::vector<double> vec = vec_from_data_source();
      if (vec.size() > 10000)
      {
        HugeStorage storage(std::move(vec));
        //do some things, but I gotta be careful I don't do anything to vec
      }
      else
      {
        SmallStorage storage(std::move(vec));
        //do some things, but I gotta be careful I don't do anything to vec
      }
      return 0;
    }
    
    此外,不要忘记C++的哲学是给你力量和(通常)设计指导原则,但是如果你真的想做到这一点,“让你开枪”。 有一些危险的,甚至是毫无意义的事情,你可以在C++中做(如果你尝试了代码< >删除/<代码>同一个指针两次),你的编译器会给你一个警告或一个错误吗?但是,语言本身并不能阻止你去做,假设你真的真的知道你在做什么。
    A x, y;
    ...
    if (complicatedCondition())
    {
        y = move(x);
    } 
    
    foo(x); // Did I move from x? And if so, is it safe to call foo()?
    

    澄清:您需要小心,不要对需要先决条件的
    vec
    执行任何操作。您可以对
    vec
    执行不需要任何先决条件的任何操作。例如,您可以指定
    vec
    一个新值。您可以调用
    vec.clear()
    。您可以调用
    vec.size()
    。但不要调用
    vec.pop_back()
    ,因为该成员函数有一个先决条件。

    变量没有被移动,只是它的值。为它指定一个新值绝对没有问题。什么“麻烦”你预见到了吗?你展示的代码的实际问题是什么?@NikBougalis更新了更现实的示例。对于库类型,不能保证对象是可分配的,只能保证它是可破坏的。也就是说,许多库实现将提供超出标准要求的额外保证s、 @DavidRodríguez dribeas:17.6.5.15/p1保证从std::lib类型移动的所有类型都将处于有效但未指定的状态。这意味着,除非这些类型在赋值语句的lhs上有一个先决条件,否则即使从中移动,也可以将其赋值。请注意,此总括语句适用于std lib中的类型,而不是一般来说,对于类型。我不知道任何std::type都有被分配到的先决条件。@HowardHinnant:谢谢你的澄清。@HowardHinnant:我错了。只是检查了标准并证实了你在这里提到的内容。我不会删除注释,因为这会使你的注释毫无意义。以防它不是显而易见的对于所有人,我要澄清的是,如果不先检查前提条件,就不能做任何需要先决条件的事情。如果(!vec.empty())vec.pop_back();
    ,调用
    vec.empty()
    ,则可以调用
    if(!vec.empty())vec.pop_back()
    
    A x, y;
    ...
    if (complicatedCondition())
    {
        y = move(x);
    } 
    
    foo(x); // Did I move from x? And if so, is it safe to call foo()?
    
    //do some things, but I gotta be careful I don't do anything to vec