現在のコンセンサスは、新しいオブジェクトを作成しないすべての ?= 演算子を最初に実装する必要があるということです。例外の安全性が問題であるか(あなたの場合はおそらくそうではない)、または目標であるかに応じて、 ?= 演算子の定義は異なる場合があります。その後、オペレーターを実装しますか?値渡しのセマンティクスを使用する ?= 演算子に関して自由な関数として。
// thread safety is not a problem
class Q
{
double w,x,y,z;
public:
// constructors, other operators, other methods... omitted
Q& operator+=( Q const & rhs ) {
w += rhs.w;
x += rhs.x;
y += rhs.y;
z += rhs.z;
return *this;
}
};
Q operator+( Q lhs, Q const & rhs ) {
lhs += rhs;
return lhs;
}
これには次の利点があります。
- ロジックの実装は 1 つだけです。クラスが変更された場合、operator?= と operator? を再実装するだけで済みます。自動的に適応します。
- フリー関数演算子は、コンパイラの暗黙的な変換に関して対称的です
- 演算子の最も効率的な実装ですか? コピーに関して見つけることができます
オペレーターの効率?
オペレーターに電話したら?2 つの要素では、3 番目のオブジェクトを作成して返す必要があります。上記のアプローチを使用すると、コピーはメソッド呼び出しで実行されます。そのままでは、一時オブジェクトを渡すときに、コンパイラはコピーを省略できます。これは、「コンパイラーはコピーを除外できる」とではなく、「コンパイラーはコピーを除外できることを認識している」と解釈する必要があることに注意してください。走行距離はコンパイラーによって異なり、同じコンパイラーでもコンパイルの実行ごとに異なる結果が得られる可能性があります (オプティマイザーで使用できるパラメーターまたはリソースが異なるため)。
a
次のコードでは、との合計で一時が作成され、最終結果で 2 番目の一時を作成するには、その一時を と一緒にb
再度渡す必要があります。operator+
c
Q a, b, c;
// initialize values
Q d = a + b + c;
値operator+
渡しのセマンティクスがある場合、コンパイラは値渡しのコピーを省略できます (コンパイラは、2 回目の呼び出しの直後に一時ファイルが破棄されることを認識しており、operator+
渡すために別のコピーを作成する必要はありません)。
コード内でoperator?
1 行の関数 ( ) として実装できたとしても、そうすべきではありません。Q operator+( Q lhs, Q const & rhs ) { return lhs+=rhs; }
その理由は、によって返されoperator?=
た参照が実際に同じオブジェクトへの参照であるかどうかをコンパイラが認識できないためです。return ステートメントがlhs
オブジェクトを明示的に受け取るようにすることで、コンパイラーは return コピーを省略できることを認識します。
型に関する対称性
T
type から typeへの暗黙的な変換があり、各型のQ
2 つのインスタンスt
およびq
それぞれがある場合、(t+q)
および(q+t)
両方が呼び出し可能であることが期待されます。operator+
内部でメンバ関数として実装するQ
と、コンパイラはt
オブジェクトを一時Q
オブジェクトに変換できず、後で(Q(t)+q)
メンバ関数を呼び出すために左側で型変換を実行できないため、呼び出すことができません。したがって、メンバー関数の実装でt+q
はコンパイルされません。
これは、算術用語で対称でない演算子にも当てはまることに注意してください。型について話しているのです。を aに昇格させることで aT
から aを差し引くことができる場合、別の自動昇格でaから aを差し引くことができない理由はありません。Q
T
Q
Q
T