9

以下に数値ベクトルテンプレートクラス(数値計算用のベクトル)があります。D=A+B+Cすべての変数がVectorオブジェクトである場所に記述できるようにしようとしています。ABおよびC変更しないでください。私の考えはVector operator+(Vector&& B)、(うまくいけば)RvalueVectorがから返されたB+C後、後続のすべての追加がそのオブジェクトに格納されるように、つまり後続のすべての追加のRvalueのストレージを盗むように使用することです。これは、新しいオブジェクトの作成と必要なストレージを排除するためです。

私の問題は、呼び出された各関数からの出力ステートメントから、呼び出されないことがわかることVector operator+(Vector&& B)です。foo(Vector& B)オーバーロードされたダミー関数を持っていて、foo(Vector&& B)を試してみるとfoo(A+B+C)、2番目の関数が期待どおりに呼び出されるため、理由がわかりません。

長い質問で申し訳ありませんが、これが私の最初の質問であり、できるだけ明確にしたいと思います。

私が明らかに間違っていること、またはなぜこれを試みるべきではないのかについての提案をいただければ幸いです。

template <typename T>
class Vector
{
        int n;
        T* v;
        Vector();
        ~Vector();
        Vector(const Vector& B);
        Vector(Vector&& B);
        inline Vector operator+(const Vector& B) const;
        inline Vector operator+(Vector&& B) const;
};

template <typename T>
Vector<T>::Vector(const Vector<T>& B)
{
        ...
}

template <typename T>
Vector<T>::Vector(Vector<T>&& B)
{
        ...
}

template <typename T>
Vector<T> Vector<T>::operator+(const Vector<T>& B) const
{
        Vector<T> C;
        ...
        return C;
}

template <typename T>
Vector<T> Vector<T>::operator+(Vector<T>&& B) const
{
        ...do stuff to B
        return B;
}
4

2 に答える 2

7

式では:

D=A+B+C

ABは左辺値なので、呼び出しA+Bは呼び出しますVector::operator(const Vector&)

それは右辺値を返します。これを と呼びましょう。tmp次の部分式はtmp+Cです。

Cも左辺値であるため、Vector::operator(const Vector&)再度呼び出します。それは別の右辺値を返します、それを呼び出しましょうtmp2

最後の部分式は ですD=tmp2が、型には移動代入演算子がないため、暗黙的に定義されたコピー代入演算子が使用されます。

つまりoperator+、右辺で右辺値を使用して呼び出すことはありません。右辺値引数を持つ唯一の式は、右辺値に対して定義していない代入です。

オーバーロードされた非メンバー演算子を定義する方が良いでしょう:

Vector operator+(const Vector&, const Vector&);
Vector operator+(Vector&&, const Vector&);
Vector operator+(const Vector&, Vector&&);
Vector operator+(Vector&&, Vector&&);

これは、右辺値と左辺値の任意の組み合わせで機能します。(一般的operator+には、とにかく通常は非会員であるべきです。)

編集:以下の代替提案は機能しません。場合によってはあいまいになります。

別の方法として、コンパイラがサポートしている場合 (clang だけがサポートしていると思います)、既存のものを保持し、参照修飾子で区別される 2 つのオーバーロードVector::operator+(Vector&&)に置き換えることもできます。Vector::operator+(const Vector&)

Vector Vector::operator+(const Vector& v) const&
{
  Vector tmp(*this);
  tmp += v;
  return tmp;
}

Vector Vector::operator+(const Vector& v)&&
{
  *this += v;
  return std::move(*this);
}

これは、右辺が右辺値である場合にしか移動セマンティクスを使用できない元のコードと比較して、右辺値であることがわかっている場合に再利用*thisします。つまり、加算の左側が右辺値である場合に移動セマンティクスを使用します。(NB 上記のコードoperator+=は、David Rodriguez の回答で提案されているようにメンバーを定義したことを前提としています)

于 2012-07-30T17:10:35.577 に答える
1

メンバーメソッドとして提供し、次のように定義されoperator+=た無料の関数からそれを再利用することをお勧めします。operator+

template <typename T>
Vector<T> operator+( Vector<T> lhs,              // by value
                     Vector<T> const & rhs ) {
    lhs += rhs;
    return lhs;
}

a + b + cとしてグループ化される呼び出しが与えられると(a+b) + c、コンパイラーはaへの最初の呼び出しのコピーを作成しoperator+、それをその場で変更し(lhs += rhs)、それを戻り値に移動します。次に、その戻り値を(移動を省略しない限り)2番目の引数に移動しoperator+、そこで再びその場で変更されてから、戻り値に移動します。

全体として、の結果を保持する単一の新しいオブジェクトが作成され、a+b+c次と同等のセマンティクスが提供されます。

Vector<T> tmp = a;
tmp += b;
tmp += c;

しかし、より優れた、よりコンパクトな構文を使用しますa + b + c


これは適切に処理されず、 2つのa + (b+c)オブジェクトが作成されることに注意してください。これをサポートする場合は、複数のオーバーロードを生成する必要があります。

于 2012-07-30T17:22:21.987 に答える