移動可能でコピー可能なクラスの保存
あなたがこのクラスを持っていると想像してください:
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の一部の古いバージョンでは定義されていません)が、ここではデバッグ出力用に定義しています。
さて、これらのクラスの1つを格納するクラスを作成したい場合は、次のように値渡しを使用できます。
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
どちらでも構いません。私は最後のテストで2つの動きを持っていますが、これは最適ではないかもしれませんが、動きは通常安価なので、それと一緒に暮らすことができます。それをより最適にするために、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; }
};
そして、constへの参照を渡します。私がそれを使うとき:
std::cout << "UnmovableDataStore test:\n";
UnmovableData umd;
UnmovableDataStore umds;
umds.setData(umd);
出力を取得します:
UnmovableDataStore test:
copy assignment
あなたが期待するようにたった1つのコピーで。
コピーできないクラスの保存
また、移動可能であるがコピー不可能なクラスを持つこともできます。
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
そして今、私たちには良い動きが1つしかないことに注意してください。
ジェネリックコンテナ
ただし、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
メソッドのオーバーロードが少なくとも2つ必要であり、重複コードが発生する可能性があります。使用法:
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
ライブデモ。お役に立てば幸いです。