2014-10-29 131 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"); 

其中如果省音将不会被允许,则移动构造函数会调用。

相关问题