2014-10-29 182 views
2

我當時正在關注這篇文章Ten C++11 Features Every C++ Developer Should Use,並在Move semantics示例的代碼中添加了一些基本跟蹤,並且看到移動構造函數從未被調用並且不知道爲什麼。我已經試過編譯器GNU 4.6.3和英特爾15.0.0,結果是一樣的。爲什麼移動構造函數在這種情況下不會被調用?

我編譯它是這樣的:

# using Intel compiler 
icpc -Wall -g -Wno-shadow -std=c++0x -o showcase ./showcase.cpp 

# using gnu g++ compiler 
g++ -Wall -g -Wno-shadow -std=gnu++0x -o showcase ./showcase.cpp 

這是輸出我得到的是不被調用的移動構造函數在那裏當它應該在133行:

instantiating b1 ... 
Buffer() default constructor invoked 
my name is: 
instantiating b2 ... 
Buffer(const std::string& name, size_t size) constructor invoked 
my name is: buf2 
instantiating b3 ... 
Buffer(const Buffer& copy) copy constructor invoked 
my name is: buf2 
instantiating b4 ... 
Buffer(const std::string& name, size_t size) constructor invoked 
my name is: buf64 
moving getBuffer<int>("buf5") to b1 ... 
Buffer(const std::string& name, size_t size) constructor invoked 
Buffer& operator=(Buffer&& temp) move assignment operator invoked 
my name is: buf5 

下面是代碼:

#include <assert.h> 
#include <iostream> 
#include <math.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string> 

#include <map> 
#include <vector> 
#include <memory> 
#include <algorithm> 

using namespace std; 

//============================================================================ 
// Classes 
//============================================================================ 

template <typename T> 
class Buffer 
{ 
    std::string   _name; 
    size_t    _size; 
    std::unique_ptr<T[]> _buffer; 

public: 
    // default constructor 
    Buffer(): 
     _size(16), 
     _buffer(new T[16]) { 
     cout << "Buffer() default constructor invoked " << endl; 
    } 

    // constructor 
    Buffer(const std::string& name, size_t size): 
     _name(name), 
     _size(size), 
     _buffer(new T[size]) { 
     cout << "Buffer(const std::string& name, size_t size) constructor invoked " << endl; 
    } 

    // copy constructor 
    Buffer(const Buffer& copy): 
     _name(copy._name), 
     _size(copy._size), 
     _buffer(new T[copy._size]) 
    { 
     cout << "Buffer(const Buffer& copy) copy constructor invoked " << endl; 
     T* source = copy._buffer.get(); 
     T* dest = _buffer.get(); 
     std::copy(source, source + copy._size, dest); 
    } 

    void print_name() const { 
     cout << "my name is: " << _name << endl; 
    } 

    // copy assignment operator 
    Buffer& operator=(const Buffer& copy) 
    { 
     cout << "Buffer& operator=(const Buffer& copy) assignment operator invoked " << endl; 
     if(this != &copy) 
     { 
     _name = copy._name; 

     if(_size != copy._size) 
     { 
      _buffer = nullptr; 
      _size = copy._size; 
      _buffer = _size > 0 ? new T[_size] : nullptr; 
     } 

     T* source = copy._buffer.get(); 
     T* dest = _buffer.get(); 
     std::copy(source, source + copy._size, dest); 
     } 

     return *this; 
    } 

    // move constructor 
    Buffer(Buffer&& temp): 
     _name(std::move(temp._name)), 
     _size(temp._size), 
     _buffer(std::move(temp._buffer)) 
    { 
     cout << "Buffer(Buffer&& temp) move constructor invoked" << endl; 
     temp._buffer = nullptr; 
     temp._size = 0; 
    } 

    // move assignment operator 
    Buffer& operator=(Buffer&& temp) 
    { 
     cout << "Buffer& operator=(Buffer&& temp) move assignment operator invoked" << endl; 
     assert(this != &temp); // assert if this is not a temporary 

     _buffer = nullptr; 
     _size = temp._size; 
     _buffer = std::move(temp._buffer); 

     _name = std::move(temp._name); 

     temp._buffer = nullptr; 
     temp._size = 0; 

     return *this; 
    } 
}; 

template <typename T> 
Buffer<T> getBuffer(const std::string& name) { 
    Buffer<T> b(name, 128); 
    return b; 
} 

//============================================================================ 
// Main 
//============================================================================ 

int main(int argc, char** argv) { 
    cout << "**************** move semantics" << endl; 
    cout << "instantiating b1 ..." << endl; 
    Buffer<int> b1; 
    b1.print_name(); 
    cout << "instantiating b2 ..." << endl; 
    Buffer<int> b2("buf2", 64); 
    b2.print_name(); 
    cout << "instantiating b3 ..." << endl; 
    Buffer<int> b3 = b2; 
    b3.print_name(); 
    cout << "instantiating b4 by moving from a temp object ..." << endl; 
    Buffer<int> b4 = getBuffer<int>("buf64"); // Buffer<int>("buf4", 64); 
    b4.print_name(); 
    cout << "moving getBuffer<int>(\"buf5\") to b1 ..." << endl; 
    b1 = getBuffer<int>("buf5"); 
    b1.print_name(); 

    return EXIT_SUCCESS; 
} 
+4

我猜你沒有看到因爲copy-elision而發生的移動 – 2014-10-29 13:46:40

+2

對於GCC使用'-fno-elide-constructors'命令行選項 – 2014-10-29 13:49:17

+0

是的,使用命令行選項'-fno-elide-constructors'解決了這個問題。我必須找到相同的icpc編譯器... – 2014-10-29 13:52:27

回答

5

正確調用移動賦值運算符。

對於您期望移動構建的情況,b4,您將獲得返回值優化(RVO),其中結果對象直接在調用方提供的存儲中構建。這是否發生取決於編譯器和選項:它是允許的但不是必需的。即這是一個實施質量問題。


請注意,這是不是一個好主意, -fno-elide-constructors以避免這種情況。 RVO比普通施工和移動施工更有效率。它必須是,因爲它更少。

+0

謝謝你的回答。我只是擔心RVO沒有發生。有沒有辦法知道它是否發生,而不是使用這個無elide-構造函數? – 2014-10-29 14:01:29

+1

您可以隨時檢查生成的機器碼。 – 2014-10-29 14:05:32

+0

當然,如果你說機器碼。我不能成爲唯一不... – 2014-10-31 00:24:52

2

在某些情況下允許複製/移動操作的省略。雖然複製或移動構造函數應該可以接受。例如,如果你將可以爲你的類的移動構造函數的私有訪問控制,則編譯器將發出至少一個錯誤對於這一說法

Buffer<int> b4 = getBuffer<int>("buf64"); 

其中如果省音將不會被允許,則移動構造函數會調用。

相關問題