2017-05-25 3 views
0

私はこの問題をより詳しく説明するためにTestClassを作成しました。 オブジェクトがベクトルにプッシュされ、コンストラクタがstd::move関数を使用して初期化されたときに、ムーブコンストラクタが呼び出されます。 しかし、ときに我々はあなたがrvalue_func()の結果値がtestvar2オブジェクトに割り当てられているが、移動コンストラクタが呼び出されていない、また、他のコンストラクタや代入演算子は...C++ 11コンストラクタを移動し、不明な方法で初期値を設定します。

呼ばれなかったことを、最後に見ることができます TestClass testvar2(rvalue_func());呼び出します

質問: rvalue_func()からtestvar2までの値を何も呼び出さずにコピーし、なぜ移動コンストラクタが呼び出されなかったのですか?

#include <iostream> 
using std::cout; 
using std::endl; 

#include <vector> 

class TestClass { 
public: 

    TestClass(int arg_x=0, int arg_y=0) : 
      values { nullptr } { 

     values = new int[2]; 
     values[0] = arg_x; 
     values[1] = arg_y; 
     cout << "default constructor " << "x = " << values[0] << " y = " 
       << values[1] << endl; 
    } 

    TestClass(const TestClass &arg) : 
      values { nullptr } { 

     values = new int[2]; 

     values[0] = arg.values[0]; 
     values[1] = arg.values[1]; 

     cout << "copy constructor " << "x = " << values[0] << " y = " 
       << values[1] << endl; 
    } 

    TestClass(TestClass &&arg) : 
      values { arg.values } { 
     arg.values = nullptr; 
     cout << "move constructor " << "x = " << values[0] << " y = " 
       << values[1] << endl; 
    } 

    TestClass &operator=(TestClass &right) { 
     cout << "assignment operator =" << endl; 
     if (this != &right) { 
      delete values; 
      values = nullptr; 
      values = new int[2]; 
      values[0] = right.values[0]; 
      values[1] = right.values[2]; 
     } 
     return *this; 
    } 

    TestClass &operator=(TestClass &&right) { 
     cout << "move assignment operator =" << endl; 
     if (this != &right) { 
      delete values; 
      values = right.values; 
      right.values = nullptr; 
     } 
     return *this; 
    } 

    void print() { 
     if (values != nullptr) 
      cout << "x = " << values[0] << " y = " << values[1] << endl; 
    } 
private: 
    int *values; 
}; 

TestClass rvalue_func() { 
    cout << "creating TestClass temp" << endl; 
    TestClass temp(100, 200); 
    cout << "TestClass temp is created" << endl; 
    return temp; 
} 
void test_rvalues() { 
    cout << "-------------vector push back--------------" << endl; 
    std::vector<TestClass> test_vector; 
    test_vector.push_back(TestClass(1, 2)); 

    cout << "-----rvalue constructor with std::move-----" << endl; 
    TestClass testvar1(std::move(rvalue_func())); 

    cout << "------------rvalue constructor-------------" << endl; 
    TestClass testvar2(rvalue_func()); 


    cout << "-------------------------------------------" << endl; 
    cout << "testvar2 values "; 
    testvar2.print(); 
} 

int main(int argc, char *argv[]) { 
    test_rvalues(); 

    return 0; 
} 

結果:

-------------vector push back-------------- 
default constructor x = 1 y = 2 
move constructor x = 1 y = 2 
-----rvalue constructor with std::move----- 
creating TestClass temp 
default constructor x = 100 y = 200 
TestClass temp is created 
move constructor x = 100 y = 200 
------------rvalue constructor------------- 
creating TestClass temp 
default constructor x = 100 y = 200 
TestClass temp is created 
------------------------------------------- 
testvar2 values x = 100 y = 200 

答えて

1

これは(これはそれが省略されている移動コンストラクタだ場合でも名前のようです)copy elisionと呼ばれる、コンパイラが実行することを許可されている最適化です。

基本的に、コンパイラは、コピーまたは移動コンストラクタを呼び出さないようにすることもできます(C++ 17以降でも必要です)。 。この場合、オブジェクトがtestvar2に入ることを知っていたので、最初にそのオブジェクトを作成しました。

通常、コンパイラの最適化は、適合しているプログラムが現在存在する最適化と存在しない最適化の違いを知らせる方法がない限り許可されます(たとえば、intの算術演算を置き換える結果はCPUが計算するのに安価です)。コピーエリートは、コンパイラがその違いを明らかにできるように最適化することが特に許可されているいくつかのケースの1つです。

関連する問題