2014-10-29 5 views
2

Я следовал этой статье Ten C++11 Features Every C++ Developer Should Use и добавил базовую трассировку в код примера Move semantics и вижу, что конструктор перемещения никогда не вызывается и не удивляется, почему. Я пробовал как с компиляторами GNU 4.6.3, так и с Intel 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

Я думаю, вы не видите перемещение из-за копирования-элиты, которое имело место –

+2

для GCC использовать опцию командной строки '-fno-elide-constructors' –

+0

Да, используя опцию командной строки' -fno-elide-constructors' решает проблему. Я должен найти то же самое для компилятора icpc ... –

ответ

5

Правильно выведен оператор назначения перемещения.

Для случая, когда вы ожидали перемещения, b4, вы получаете оптимизацию возвращаемого значения (RVO), где объект результата напрямую создается в хранилище, предоставляемом вызывающим абонентом. Это зависит от компилятора и параметров: это разрешено, но не требуется. То есть это проблема качества выполнения.


Обратите внимание, что это не рекомендуется использовать, например. -fno-elide-constructors, чтобы этого избежать. RVO намного эффективнее, чем обычная конструкция плюс конструкция перемещения. Это должно быть, поскольку это меньше.

+0

Благодарим вас за ответ. Я просто волновался, что RVO не происходит. Есть ли способ узнать, происходит ли это иначе, чем использовать этот конструктор no-elide? –

+1

Вы всегда можете проверить сгенерированный машинный код. –

+0

Конечно, если вы говорите машинный код. Я не могу быть единственным человеком, который не ... –

2

В некоторых случаях разрешено копирование/перемещение. Хотя конструкторы копирования или перемещения должны быть приемлемыми. Например, если вы установите частный контроль доступа для конструктора перемещения в классе, то компилятор выдаст ошибку, по крайней мере для этого утверждения

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

где, если элизия не будет разрешена, то конструктор движения будет называется.