为什么在这种情况下不调用移动构造函数?
Why the move constructor doesn't get invoked in this case?
我一直在关注这篇文章《每个C++开发人员都应该使用的十个C++11特性》,并在Move语义示例的代码中添加了一些基本的跟踪,发现Move构造函数从未被调用,并想知道为什么。我试过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 != ©)
{
_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;
}
移动分配运算符被正确调用。
对于预期移动构造b4
的情况,您将获得返回值优化(RVO),其中结果对象直接在调用方提供的存储中构造。是否发生这种情况取决于编译器和选项:这是允许的,但不是必需的。也就是说,这是一个实施质量问题。
请注意,使用例如-fno-elide-constructors
来避免这种情况不是一个好主意。RVO比普通建筑加移动建筑效率高得多。它必须是,因为它更少。
在某些情况下允许省略复制/移动操作。尽管复制或移动构造函数是可以接受的。例如,如果您将为类中的move构造函数设置私有访问控制,那么编译器将至少为以下语句发出错误
Buffer<int> b4 = getBuffer<int>("buf64");
其中,如果不允许省略,则将调用move构造函数。
相关文章:
- 为什么不调用移动构造函数?(默认情况下只有构造器,没有别的)
- std::vector::p ush_back() 不会在 MSVC 上编译具有已删除移动构造函数的对象
- 仅包含可移动 std::map 的类的移动构造函数不起作用
- 为什么调用复制构造函数而不是移动构造函数?
- 基类中的默认析构函数禁用子类中的移动构造函数(如果有成员)
- 从具有按值捕获的 lambda 移动构造 std::函数时,移动构造函数调用两次
- 移动构造函数和右值引用
- 为什么 std::memmove 中联合的默认非平凡移动构造函数C++?
- 具有专用化的模板类中的可靠条件复制和移动构造函数
- C++:为什么不调用移动构造函数?
- 移动构造函数永远不会被调用
- C++:关于使用 Stroustrup 示例移动构造函数/赋值的问题
- 运算符+ 的规范实现涉及额外的移动构造函数
- C ++为什么在移动构造函数中需要移动/前进
- 为什么在删除"移动构造函数"时使用"复制构造函数"?
- 为什么这里不调用移动构造函数?
- 隐式移动构造函数
- 如何为具有私有成员的派生类实现移动构造函数
- 为什么不调用移动构造函数
- 是否可以避免在以下代码中复制/移动构造函数的需要?