共有リソースを保持するクラスの次の単純化された例を検討してください。
class array
{
typedef std::array<float, 1000> resource;
public:
// default constructor, creates resource
array() : p_(std::make_shared<resource>()), a_(0), b_(1000) {}
// create view
array operator()(int a, int b) { return array(p_, a_+a, a_+b); }
// get element
float& operator[](int i) { return (*p_)[a_+i]; }
private:
std::shared_ptr<resource> p_;
int a_, b_;
// constructor for views
array(std::shared_ptr<resource> p, int a, int b) : p_(p), a_(a), b_(b) {}
};
今、ユーザーを混乱させないこのクラスのセマンティクスを定義する方法を考えています。operator=()
たとえば、要素のコピーを許可したい:
array x, y;
x = y; // copies all elements from y's storage to x's
x(30,40) = y(50,60); // copies 10 elements
しかし、一貫性を保つために、コピー コンストラクターとコピー代入演算子は常にコピーすべきではないでしょうか。どうですか:
array z = x(80,90); // will create an array referencing x's storage
この場合、コピーはコンパイラによって省略されるため、コピー代入演算子が何を行っても、のストレージz
への参照が保持されます。x
これを回避する方法はありません。
では、割り当てが常に参照を作成し、コピーが明示的に宣言される方が理にかなっているでしょうか? たとえば、copy_wrapper
要素のコピーを強制するラッパー クラスを定義できます。
class copy_wrapper
{
array& a_;
public:
explicit copy_wrapper(array& a) : a_(a) {}
array& get() { return a_; }
};
class array
{
// ...
array& operator=(const array& a); // references
array& operator=(copy_wrapper c); // copies
copy_wrapper copy() { return copy_wrapper(*this); }
};
これにより、ユーザーは次のように書く必要があります。
array x, y;
x(30,40) = y(50,60).copy(); // ok, copies
x(30,40) = y(50,60); // should this throw a runtime error?
x = y; // surprise! x's resource is destructed.
少し面倒で、それよりも悪いのは、あなたが期待するものではありません.
このあいまいさをどのように処理すればよいでしょうか。