C++ 11右值引用也调用了拷贝构造函数

Luk*_*ens 24 c++ rvalue-reference move-constructor c++11

我一直在测试一些C++ 11的一些功能.我遇到了r值引用并移动构造函数.

我实现了我的第一个移动构造函数,这里是:

#include <iostream>
#include <vector>
using namespace std;

class TestClass{

public:
    TestClass(int s):
        size(s), arr(new int[s]){
    }
    ~TestClass(){
        if (arr)
            delete arr;
    }
    // copy constructor
    TestClass(const TestClass& other):
            size(other.size), arr(new int[other.size]){
        std::copy(other.arr, other.arr + other.size, arr);
    }

    // move constructor
    TestClass(TestClass&& other){
        arr=other.arr;
        size=other.size;

        other.arr=nullptr;
        other.size=0;
    }

private:
    int size;
    int * arr;
};

int main(){
    vector<TestClass> vec;

    clock_t start=clock();
    for(int i=0;i<500000;i++){
        vec.push_back(TestClass(1000));
    }
    clock_t stop=clock();
    cout<<stop-start<<endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

代码工作正常.无论如何把一个std :: cout放在复制构造函数中我注意到它被调用了!并且很多次..(移动构造函数500000次,复制构造函数524287次).

让我感到惊讶的是,如果我从代码中注释掉复制构造函数,整个程序会更快,而这次移动构造函数被称为1024287次.

任何线索?

How*_*ant 33

穿上noexcept你的移动构造函数:

TestClass(TestClass&& other) noexcept {
Run Code Online (Sandbox Code Playgroud)

详细说明:我打算给这一个皮埃尔,但不幸的是,cppreference源只是大致正确.

在C++ 03中

vector<T>::push_back(T)
Run Code Online (Sandbox Code Playgroud)

有"强烈的例外保证".这意味着如果push_back抛出异常,向量将保持与调用之前相同的状态push_back.

如果移动构造函数抛出异常,则此保证会出现问题.

vector重新分配,就喜欢移动的元素从旧的缓冲区,以新的.但是,如果这些移动中的任何一个抛出异常(除了第一个),那么它将处于旧缓冲区已被修改的状态,并且新缓冲区尚未包含它应该包含的所有内容.在vector旧的缓冲区不能恢复到原来的状态,因为它会移动元素回做,这些举动也可能会失败.

因此为C++ 11制定了一条规则:

  1. 如果T有一个noexcept移动构造函数,可用于将元素从旧缓冲区移动到新缓冲区.

  2. 否则,如果T有一个复制构造函数,将使用它.

  3. 否则(如果没有可访问的拷贝构造函数),则毕竟将使用移动构造函数,但是在这种情况下,不再给出强异常安全保证.

澄清:规则2中的"复制构造函数"是指构造函数const T&,而不是那些所谓的T&复制构造函数.:-)


Pie*_*aud 14

使用noexcept您的移动构造函数:

TestClass(TestClass&& other) noexcept { ... }
Run Code Online (Sandbox Code Playgroud)

noexcept没有像这样的常量表达式相当于noexcept(true).

编译器可以使用此信息对非抛出函数启用某些优化,并启用noexcept运算符,该运算符可以在编译时检查是否声明了特定表达式抛出任何异常.

例如,如果元素的移动构造函数是noexcept,则std :: vector等容器将移动它们的元素,否则复制.

资料来源:http://en.cppreference.com/w/cpp/language/noexcept_spec

注意:这是一个C++ 11功能.某些编译器可能还没有实现它...(例如:Visual Studio 2012)