注释掉“移动构造函数”和“移动赋值运算符”会导致编译错误
Commenting out `move constructor` and `move assignment operator` makes compilation error
我从 Ten C++11 Features Every C++ Developer Should Use 中获取了以下代码。我想查看带/不带 move constructor
和 move assignment operator
的输出。原始代码编译得很好。但是如果我注释掉这两种方法的实现,它就无法编译并出现错误:
move-1.cpp: In instantiation of ‘Buffer<T>& Buffer<T>::operator=(const Buffer<T>&) [with T = int]’:
move-1.cpp:90:6: required from here
move-1.cpp:40:17: error: no match for ‘operator=’ (operand types are ‘std::unique_ptr<int [], std::default_delete<int []> >’ and ‘int*’)
_buffer = _size > 0 ? new T[_size] : nullptr;
编译器:gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.4)
代码如下:
#include <cassert>
#include <iostream>
#include <memory>
#include <string>
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]) {
std::cout << "default constructor\n";
}
// constructor
Buffer(const std::string &name, size_t size)
: _name(name), _size(size), _buffer(new T[size]) {
std::cout << "param constructor\n";
}
// copy constructor
Buffer(const Buffer ©)
: _name(copy._name), _size(copy._size), _buffer(new T[copy._size]) {
T *source = copy._buffer.get();
T *dest = _buffer.get();
std::copy(source, source + copy._size, dest);
std::cout << "copy constructor\n";
}
// copy assignment operator
Buffer &operator=(const Buffer ©) {
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);
std::cout << "copy assignment\n";
}
return *this;
}
// move constructor
Buffer(Buffer &&temp)
: _name(std::move(temp._name)), _size(temp._size),
_buffer(std::move(temp._buffer)) {
temp._buffer = nullptr;
temp._size = 0;
std::cout << "move constructor\n";
}
// move assignment operator
Buffer &operator=(Buffer &&temp) {
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;
std::cout << "move assignment\n";
return *this;
}
};
template <typename T> Buffer<T> getBuffer(const std::string &name) {
Buffer<T> b(name, 128);
return b;
}
int main() {
Buffer<int> b1;
Buffer<int> b2("buf2", 64);
Buffer<int> b3 = b2;
Buffer<int> b4 = getBuffer<int>("buf4");
b1 = getBuffer<int>("buf5");
return 0;
}
在 main
中,调用了以下 constructors/assignment 运算符:
Buffer<int> b1; // default constructor
Buffer<int> b2("buf2", 64); // constructor
Buffer<int> b3 = b2; // copy constructor
Buffer<int> b4 = getBuffer<int>("buf4"); // move constructor
b1 = getBuffer<int>("buf5"); // move assignment operator
删除移动操作后,这将变为:
Buffer<int> b1; // default constructor
Buffer<int> b2("buf2", 64); // constructor
Buffer<int> b3 = b2; // copy constructor
Buffer<int> b4 = getBuffer<int>("buf4"); // copy constructor
b1 = getBuffer<int>("buf5"); // copy assignment operator
最后一行 (b1 = getBuffer<int>("buf5");
) 暴露了您的复制赋值运算符中的一个问题:您不能使用 operator=
将原始指针分配给 unique_ptr
。相反,使用 reset
或构造一个新的 unique_ptr
:
// copy assignment operator
Buffer &operator=(const Buffer ©) {
if (this != ©) {
_name = copy._name;
if (_size != copy._size) {
_buffer = nullptr;
_size = copy._size;
if (_size > 0) {
_buffer.reset(new T[_size]);
}
}
T *source = copy._buffer.get();
T *dest = _buffer.get();
std::copy(source, source + copy._size, dest);
std::cout << "copy assignment\n";
}
return *this;
}
为了即使在包含移动构造函数时也能捕捉到这一点,我会在 main
中添加一个强制使用复制赋值运算符的新测试,例如:
// ...
b1 = getBuffer<int>("buf5"); // move assignment operator
b2 = b3; // copy assignment operator
我从 Ten C++11 Features Every C++ Developer Should Use 中获取了以下代码。我想查看带/不带 move constructor
和 move assignment operator
的输出。原始代码编译得很好。但是如果我注释掉这两种方法的实现,它就无法编译并出现错误:
move-1.cpp: In instantiation of ‘Buffer<T>& Buffer<T>::operator=(const Buffer<T>&) [with T = int]’:
move-1.cpp:90:6: required from here
move-1.cpp:40:17: error: no match for ‘operator=’ (operand types are ‘std::unique_ptr<int [], std::default_delete<int []> >’ and ‘int*’)
_buffer = _size > 0 ? new T[_size] : nullptr;
编译器:gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.4)
代码如下:
#include <cassert>
#include <iostream>
#include <memory>
#include <string>
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]) {
std::cout << "default constructor\n";
}
// constructor
Buffer(const std::string &name, size_t size)
: _name(name), _size(size), _buffer(new T[size]) {
std::cout << "param constructor\n";
}
// copy constructor
Buffer(const Buffer ©)
: _name(copy._name), _size(copy._size), _buffer(new T[copy._size]) {
T *source = copy._buffer.get();
T *dest = _buffer.get();
std::copy(source, source + copy._size, dest);
std::cout << "copy constructor\n";
}
// copy assignment operator
Buffer &operator=(const Buffer ©) {
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);
std::cout << "copy assignment\n";
}
return *this;
}
// move constructor
Buffer(Buffer &&temp)
: _name(std::move(temp._name)), _size(temp._size),
_buffer(std::move(temp._buffer)) {
temp._buffer = nullptr;
temp._size = 0;
std::cout << "move constructor\n";
}
// move assignment operator
Buffer &operator=(Buffer &&temp) {
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;
std::cout << "move assignment\n";
return *this;
}
};
template <typename T> Buffer<T> getBuffer(const std::string &name) {
Buffer<T> b(name, 128);
return b;
}
int main() {
Buffer<int> b1;
Buffer<int> b2("buf2", 64);
Buffer<int> b3 = b2;
Buffer<int> b4 = getBuffer<int>("buf4");
b1 = getBuffer<int>("buf5");
return 0;
}
在 main
中,调用了以下 constructors/assignment 运算符:
Buffer<int> b1; // default constructor
Buffer<int> b2("buf2", 64); // constructor
Buffer<int> b3 = b2; // copy constructor
Buffer<int> b4 = getBuffer<int>("buf4"); // move constructor
b1 = getBuffer<int>("buf5"); // move assignment operator
删除移动操作后,这将变为:
Buffer<int> b1; // default constructor
Buffer<int> b2("buf2", 64); // constructor
Buffer<int> b3 = b2; // copy constructor
Buffer<int> b4 = getBuffer<int>("buf4"); // copy constructor
b1 = getBuffer<int>("buf5"); // copy assignment operator
最后一行 (b1 = getBuffer<int>("buf5");
) 暴露了您的复制赋值运算符中的一个问题:您不能使用 operator=
将原始指针分配给 unique_ptr
。相反,使用 reset
或构造一个新的 unique_ptr
:
// copy assignment operator
Buffer &operator=(const Buffer ©) {
if (this != ©) {
_name = copy._name;
if (_size != copy._size) {
_buffer = nullptr;
_size = copy._size;
if (_size > 0) {
_buffer.reset(new T[_size]);
}
}
T *source = copy._buffer.get();
T *dest = _buffer.get();
std::copy(source, source + copy._size, dest);
std::cout << "copy assignment\n";
}
return *this;
}
为了即使在包含移动构造函数时也能捕捉到这一点,我会在 main
中添加一个强制使用复制赋值运算符的新测试,例如:
// ...
b1 = getBuffer<int>("buf5"); // move assignment operator
b2 = b3; // copy assignment operator