存储可移动和可复制的类
想象一下你有这个类:
class Data {
public:
Data() { }
Data(const Data& data) { std::cout << " copy constructor\n";}
Data(Data&& data) { std::cout << " move constructor\n";}
Data& operator=(const Data& data) { std::cout << " copy assignment\n"; return *this;}
Data& operator=(Data&& data) { std::cout << " move assignment\n"; return *this;}
};
注意,一个好的 C++11 编译器应该为你定义所有这些函数(一些旧版本的Visual Studio 没有),但我在这里定义它们是为了调试输出。
现在,如果您想编写一个类来存储这些类之一,我可能会像您建议的那样使用按值传递:
class DataStore {
Data data_;
public:
void setData(Data data) { data_ = std::move(data); }
};
我正在利用C++11 移动语义将值移动到所需位置。然后我可以DataStore
像这样使用它:
Data d;
DataStore ds;
std::cout << "DataStore test:\n";
ds.setData(d);
std::cout << "DataStore test with rvalue:\n";
ds.setData(Data{});
Data d2;
std::cout << "DataStore test with move:\n";
ds.setData(std::move(d2));
具有以下输出:
DataStore test:
copy constructor
move assignment
DataStore test with rvalue:
move assignment
DataStore test with move:
move constructor
move assignment
这很好。我在上次测试中有两个动作可能不是最佳的,但动作通常很便宜,所以我可以忍受。为了使它更优化,我们需要重载setData
我们稍后会做的函数,但这可能是过早的优化。
存储一个不可移动的类
但现在想象我们有一个可复制但不可移动的类:
class UnmovableData {
public:
UnmovableData() { }
UnmovableData(const UnmovableData& data) { std::cout << " copy constructor\n";}
UnmovableData& operator=(const UnmovableData& data) { std::cout << " copy assignment\n"; return *this;}
};
在 C++11 之前,所有类都是不可移动的,所以希望今天能在野外找到很多类。如果我需要编写一个类来存储它,我就不能利用移动语义,所以我可能会写这样的东西:
class UnmovableDataStore {
UnmovableData data_;
public:
void setData(const UnmovableData& data) { data_ = data; }
};
并通过引用到常量。当我使用它时:
std::cout << "UnmovableDataStore test:\n";
UnmovableData umd;
UnmovableDataStore umds;
umds.setData(umd);
我得到输出:
UnmovableDataStore test:
copy assignment
如您所料,只有一份副本。
存储不可复制的类
你也可以有一个可移动但不可复制的类:
class UncopyableData {
public:
UncopyableData() { }
UncopyableData(UncopyableData&& data) { std::cout << " move constructor\n";}
UncopyableData& operator=(UncopyableData&& data) { std::cout << " move assignment\n"; return *this;}
};
std::unique_ptr
是可移动但不可复制的类的示例。在这种情况下,我可能会编写一个类来存储它,如下所示:
class UncopyableDataStore {
UncopyableData data_;
public:
void setData(UncopyableData&& data) { data_ = std::move(data); }
};
我通过右值引用传递并像这样使用它:
std::cout << "UncopyableDataStore test:\n";
UncopyableData ucd;
UncopyableDataStore ucds;
ucds.setData(std::move(ucd));
具有以下输出:
UncopyableDataStore test:
move assignment
注意我们现在只有一个好的动作。
通用容器
然而,STL 容器需要是通用的,它们需要与所有类型的类一起工作并尽可能优化。如果你真的需要上面数据存储的通用实现,它可能看起来像这样:
template<class D>
class GenericDataStore {
D data_;
public:
void setData(const D& data) { data_ = data; }
void setData(D&& data) { data_ = std::move(data); }
};
通过这种方式,无论我们使用不可复制或不可移动的类,我们都可以获得最佳性能,但我们必须至少有两个setData
可能引入重复代码的方法重载。用法:
std::cout << "GenericDataStore<Data> test:\n";
Data d3;
GenericDataStore<Data> gds;
gds.setData(d3);
std::cout << "GenericDataStore<UnmovableData> test:\n";
UnmovableData umd2;
GenericDataStore<UnmovableData> gds3;
gds3.setData(umd2);
std::cout << "GenericDataStore<UncopyableData> test:\n";
UncopyableData ucd2;
GenericDataStore<UncopyableData> gds2;
gds2.setData(std::move(ucd2));
输出:
GenericDataStore<Data> test:
copy assignment
GenericDataStore<UnmovableData> test:
copy assignment
GenericDataStore<UncopyableData> test:
move assignment
现场演示。希望有帮助。