3

共有リソースを保持するクラスの次の単純化された例を検討してください。

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.

少し面倒で、それよりも悪いのは、あなたが期待するものではありません.

このあいまいさをどのように処理すればよいでしょうか。

4

1 に答える 1

1

割り当てが常に参照を作成し、コピーが明示的に宣言される方が理にかなっていますか?

実際には、いいえa = b開発者がコピーしないことを覚えていることを期待することはstd::auto_ptr、熱湯に入ったものです。C++11 標準委員会はまた、開発者が「私はコピーを作成していません」と明示的に言うことを許可することstd::moveを決定しました。std::forward

どうでしょうか:array z = x(80,90);この場合、コピーはコンパイラによって省略されるため、コピー代入演算子が何を行っても、z は x のストレージへの参照を保持します。これを回避する方法はありません。

これはあなたの景品でした。「配列」と「配列へのビュー」の動作を変えたいのですが、両方を class で表していますarray

arrayview同様のパブリック インターフェイスを持つ新しいクラスを実装し、範囲a_b_を に移動しますarrayview

array x, y;
x = y;                  // "array = array" copies all elements
x(30,40) = y(50,60);    // "arrayview = arrayview" copies element range
arrayview v = x(80,90); // "arrayview(const arrayview&) shallow copies
x(30,40) = y;           // NOT DEFINED, since this has no clear meaning
array z = x(80,90);     // NOT DEFINED, since this has no clear meaning

代入演算子では、同じリソースを使用してarrayviewからコピーすることは有効である必要がありますが、その特定のケースでは、 の代わりに が必要かどうかを確認する必要があることに注意してください。arrayviewstd::copy_backwardstd::copy

于 2012-12-27T17:25:45.283 に答える