B::operator*
あなたが書いた方法では、少し遅く実行されると思います。これは、 の「内部」実装A::operator*
が次のようになっているためです。
inline A A::operator*(A* this, A b)
{
A out;
out.x = this->x * b.x;
out.y = this->y * b.y;
return out;
}
そのA
ため、左側の引数へのポインターを関数に渡しますが、関数をB
呼び出す前にそのパラメーターのコピーを作成する必要があります。どちらも右側のパラメーターのコピーを作成する必要があります。
参照を使用して記述し、それを修正した場合、コードははるかに優れたものになり、おそらくA
andに対して同じものを実装するでしょう。B
const
struct A
{
float x, y;
inline A operator*(const A& b) const
{
A out;
out.x = x * b.x;
out.y = y * b.y;
return out;
}
}
struct B
{
float x, y;
}
inline B operator*(const B& a, const B& b)
{
B out;
out.x = a.x * b.x;
out.y = a.y * b.y;
return out;
}
結果は事実上一時的なものであるため、参照ではなくオブジェクトを返す必要があります (変更された既存のオブジェクトを返すわけではありません)。
補遺
ただし、両方の引数の const 参照渡しを使用すると、B では、逆参照により、A よりも効果的に高速になりますか?
まず、すべてのコードを綴ると、どちらも同じ逆参照を伴います。( のメンバーへのアクセスはthis
、ポインターの逆参照を意味することに注意してください。)
ただし、その場合でも、コンパイラがどれほど賢いかによって異なります。この場合、構造体を調べて、2 つのフロートであるためレジスタに格納できないと判断したとします。したがって、ポインタを使用してそれらにアクセスします。したがって、逆参照されたポインターのケース (参照が実装されるもの) が最善です。アセンブリは次のようになります (これは疑似アセンブリ コードです)。
// Setup for the function. Usually already done by the inlining.
r1 <- this
r2 <- &result
r3 <- &b
// Actual function.
r4 <- r1[0]
r4 <- r4 * r3[0]
r2[0] <- r4
r4 <- r1[4]
r4 <- r4 * r3[4]
r2[4] <- r4
これは、RISC のようなアーキテクチャ (ARM など) を想定しています。x86 はおそらく少ないステップを使用しますが、とにかく命令デコーダーによってこのレベルの詳細に拡張されます。ポイントは、レジスター内のポインターのすべての固定オフセット逆参照であり、それが得られるのとほぼ同じ速度であるということです。オプティマイザーは、より賢く、複数のレジスターに渡ってオブジェクトを実装しようとすることができますが、その種のオプティマイザーを作成するのは非常に困難です。result
(保存されていない単なる一時的なオブジェクトであれば、LLVM タイプのコンパイラ/オプティマイザがその最適化を簡単に実行できるのではないかと、密かに疑っていますが。)
したがって、を使用しthis
ているため、暗黙的なポインター逆参照があります。しかし、オブジェクトがスタック上にある場合はどうなるでしょうか? 役に立ちません。スタック変数は、スタック ポインター (使用されている場合はフレーム ポインター) の固定オフセット逆参照になります。したがって、コンパイラがオブジェクトを取得して複数のレジスタに分散するのに十分なほど明るくない限り、最終的にどこかでポインターを逆参照しています。
-S
オプションを自由に渡してgcc
、最終コードの逆アセンブリを取得して、実際に何が起こっているかを確認してください。