如何用move语义来解决这个问题



这是我的示例代码:

#include <iostream>
template <class T>
struct Message {
public:
T data;
Message(const T& data) {
this->data = data;
};
Message(T&& data) {
this->data = std::move(data);
}
};
template <typename T>
class Store {
public:
Store() {
segment_ = nullptr;
}
void Add(const T& d) {
segment_ = new Message<T>(d);
}
void Add(T&& d) {
segment_ = new Message<T>(d);
}
private:
Message<T>* segment_;
};
struct MoveOnlyExample {
MoveOnlyExample() = delete;
explicit MoveOnlyExample(int) {}
MoveOnlyExample(MoveOnlyExample&&) = default;
MoveOnlyExample(const MoveOnlyExample&) = delete;
~MoveOnlyExample() {}
MoveOnlyExample& operator=(MoveOnlyExample&&) = default;
MoveOnlyExample& operator=(const MoveOnlyExample&) = delete;
};
int main() {
Store<int> int_store;
int_store.Add(1);
Store<MoveOnlyExample> store;
MoveOnlyExample m{1};
store.Add(std::move(m));
return 0;
}

我有两个错误:

main.cpp:8:5: error: call to deleted constructor of 'MoveOnlyExample'
Message(const T& data) {
^
main.cpp:29:24: note: in instantiation of member function 'Message<MoveOnlyExample>::Message' requested here
segment_ = new Message<T>(d);
^
main.cpp:51:11: note: in instantiation of member function 'Store<MoveOnlyExample>::Add' requested here
store.Add(std::move(m));
^
main.cpp:36:5: note: 'MoveOnlyExample' has been explicitly marked deleted here
MoveOnlyExample() = delete;
^
main.cpp:9:20: error: overload resolution selected deleted operator '='
this->data = data;
~~~~~~~~~~ ^ ~~~~
main.cpp:42:22: note: candidate function has been explicitly deleted
MoveOnlyExample& operator=(const MoveOnlyExample&) = delete;
^
main.cpp:41:22: note: candidate function not viable: 1st argument ('const MoveOnlyExample') would lose const qualifier
MoveOnlyExample& operator=(MoveOnlyExample&&) = default;
^

为什么以及如何修复它们?

这个错误是在编译类Message<MoveOnlyExample>时引起的。当你写:

T data;
...
Message(T&& data) {
this->data = std::move(data);
}

编译器必须首先默认构造数据成员,然后才使用移动赋值。如果你想使用移动构造函数,你必须使用初始化习惯用法:

T data;
...
Message(T&& data): data(std::move(data)) {}

这里data成员确实是移动构造的。

但这还不是全部。正如@SebastianHoffmann所说,你必须在每个地方重复std::move

void Add(T&& d) {
segment_ = new Message<T>(std::move(d));
}

您的问题在于Add()方法:

void Add(T&& d) {
segment_ = new Message<T>(d);
}

移动语义不传播就编译器而言,d是一个左值,因此new Message<T>(d)调用复制构造函数。

你必须明确地告诉编译器你想再次从d移动,即:

void Add(T&& d) {
segment_ = new Message<T>(std::move(d));
}

最新更新