编译器何时在C++中移动/复制?

use*_*269 2 c++ c++11

我试图更好地理解移动在C++中是如何工作的.编译器如何知道何时移动以及何时不移动?

#include <iostream>

template <typename T>
struct Container {
    T value;
    Container (T value): value(value) {}
    Container (const Container & i): value(i.value) {
        std::cout << "copying" << std::endl;
    }
    Container (Container && i): value(std::move(i.value)) {
        std::cout << "moving" << std::endl;
    }
};

void increment (Container<int> n) {
    std::cout << "incremented to " << ++n.value << std::endl;
}

int main () {
    Container<int> n (5);
    increment(std::move(n));
    std::cout << n.value << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

这个例子打印

moving
incremented to 6
5
Run Code Online (Sandbox Code Playgroud)

所以我希望它int已被移动,但之后我不能再使用它(并获得原始值).

好的,也许int是因为value(std::move(i.value))复制它在移动构造函数中而被复制了.但我仍然不明白为什么Container<int> n它仍然存在之后它肯定被感动了.

dav*_*mac 5

std::move实际上只是将值更改为可以移动的右值.如果你将int它应用于它没有实际效果,因为"移动"一个int值没有任何意义.(确实,该值仍然是一个右值;它只是对一个rvalue引用int通常没有比对a的任何其他类型的引用更有用int).

这是因为移动意味着将资源从一个对象转移到另一个对象,这样就避免了复制这些资源(通过复制它们)的需要 - 因为这种复制可能是非平凡的; 一方面,它可能需要动态内存分配.复制int值是微不足道的,因此不需要特殊的移动语义.

因此,应用于您的示例,移动Container<int>它与复制它完全相同,当然除了输出("移动"与"复制").

(请注意,即使移动也要求源对象在操作完成后保持有效状态 - 它不会破坏源对象,因为您似乎认为它应该是这样).

至于编译器如何知道何时可以移动vs复制,这是类型类别的问题.您的使用std::move专门将值的类型类别更改为rvalue(或更具体地说是xvalue),并且此类型的值可以与移动构造函数中的rvalue引用参数匹配.通常,具有右值参考参数的重载优于具有非右值参考参数的重载(精确规则很复杂).

产生右值的另一种常见方法是作为一个未命名的临时方法 - 通过对结果未绑定到变量的对象或值执行某些操作(a + b其中a和b都是类型int,是一个简单的示例 - 结果是临时对象;它不存在于自己的变量中).当一个更复杂的对象是临时对象时,将其移动到其最终目标可能比复制它更有效,并且是安全的,因为之后不能使用移动对象的不确定状态.因此,这些值也是rvalues并将绑定到rvalue引用(并且可以移动).