特殊なケース:2つの変数の値を交換する
(一般的な解決策については、以下を参照してください。)
C ++で2つの変数の値を交換するには、常に次を使用する必要がありますswap
。
using std::swap;
swap(x, y); // Do NOT say: std::swap(x, y) -- Read about Koenig lookup!
それがどのように行われるかを気にしないでください。それは非常に速くそれをします。C ++標準ライブラリの実装は、プロセッサがサポートしている場合、これを単一の命令に最適化するために最善を尽くします(ただし、標準は実装にそうするように指示していません)。レジスターのみの変数の場合、たとえば、xchg
可能な限り高速に実行するx86命令があります。「3つのxor演算」で微調整しようとしないでください。これ以上速くなることはありません。運が悪ければ、のようなものには最適化されませんxchg
。
swap
C ++ 03の一般的な操作では、一時変数が導入され、3つのコピー構築が実行されます。C ++ 11には移動セマンティクスがあり、オブジェクトはコピーされるのではなく移動されます。独自のタイプの場合、たとえば、実際のデータへのポインターのみを保持するデータ構造の場合、この手順を最適化して、一定時間で実行されるようにする必要があります。
C ++ 03では、名前空間に独自の関数を特殊std::swap
化または実装して(この質問の上位2つの回答を参照)、スワッピングを最適化できます。クラスの各メンバーをスワッピングしてデータをスワッピングするだけです。ポインタのみを保持するデータ構造の例では、ポインタを交換するだけです。swap
C ++ 11には、新しい移動セマンティクスがあります。これにより、あるオブジェクトから別のオブジェクトへのデータの移動を実装できます。これにより、非常によく似た動作が得られます。(2つのオブジェクトの交換など、より一般的な問題のために移動セマンティクスが導入されました。1つのオブジェクトが不要になったが、別のオブジェクトは最初のオブジェクトの「コピー」である必要がある場合は、単純に移動できます。)移動セマンティクスと詳細については、コンストラクターを移動してください。
C++03とC++11の両方に、別の方法があります。暗黙的に共有されるデータと、データ構造などの重いクラスのコピーオンライトを実装できます。上記の例では、データ構造が実際のデータへのポインターを保持している場合、参照カウントを実装します。データ構造をコピーするときは、参照カウンターを1つ増やすだけです。データを変更するときは、共有されていないことを確認してください(ref count = 1)。そうでない場合は、データをコピーして「デタッチ」してください。これにより、一定時間のコピーおよびスワップ操作が発生します。
一般的な場合:複数の任意の式
のように入出力に依存しない他のステートメントの場合(a, b) = (x, y)
は、「そのまま」と書くだけで、依存関係がないため、少なくとも完全にパイプラインで実行されます。
a = x;
b = y;
編集の例のように、それらが入出力に依存している場合は、それを分割して一時的なものを導入することができます。xor-ingのようないくつかの派手な表現のトリックでそのようなことを解決しようとしても、あなたは自分に有利になることはありません。コンパイラはアセンブラの多くのトリック(のようなxchg
)を知っていますが、あなたはそれをプレーンC ++(xorのような)で表現するためのトリックしか知りません。
C ++ 11にはstd::tuple
、std::tie
一時的なものを導入せずに複数の式を割り当てることができます(それらは、タプルに格納された値を保持するために舞台裏で導入され、完全に離れて、または少なくとも保持するレジスタを使用してそれらを最適化しようとします可能であればそれら):
using std::tie;
using std::make_tuple;
tie(ones, twos) = make_tuple((ones ^ n) ^ ~twos, (ones & n) | (twos & ~n));
ここでは変換が暗黙的ではないため、右側のペア/タプルのタイプは左側のターゲット値と一致する必要があることに注意してください。問題が発生した場合はstatic_cast
、右側でを実行するか、明示的な型を指定するか、次のように明示的な型を要求するstd::make_tuple
ためにコンストラクターを使用してください。std::tuple
using std::tie;
using std::tuple;
tie(ones, twos) = tuple<int,int>((ones ^ n) ^ ~twos, (ones & n) | (twos & ~n));