演算子の優先順位

この本を通して使用してきたように、式は複数の演算子で連鎖させることができる。例えば、次の行には3つの演算子で連鎖された4つの式が含まれている。

a = b + c * d    // 3つの演算子: =、+および*
D

演算子の優先順位ルールは、連結された演算子が実行される順序と、それらが使用する式を指定する。演算子は、優先順位の高いものから低いものの順に実行される。

以下は、Dの演算子の優先順位表だ。演算子は、優先順位の高いものから低いものの順にリストされている。同じ行にあるものは、同じ優先順位を持つ。 (表内の行の折り返しは意味がない。例えば、==!isは同じ優先順位を持つ。) 特に指定がない限り、演算子は左結合だ

表で使用されている用語の一部は、以下で説明している。

演算子説明
!テンプレートインスタンス化チェーンできない
=>ラムダ定義実際の演算子ではない。テーブルに2回出現する。この行は左側の結合力を表す。
. ++ -- ( [後置演算子(および[は、それぞれ)および]とバランスが取れていなければならない
^^パワー演算子右結合
++ -- * + - ! & ~ cast一項演算子
* / %二項演算子
+ - ~二項演算子
<< >> >>>ビットシフト演算子
== != > < >= <= in !in is !is比較演算子ビット演算子に対して順序付けられておらず、連鎖できない
&ビット単位のAND比較演算子に対して順序なし
^ビット単位の排他的論理和比較演算子に対して順序なし
|ビット単位のOR比較演算子に対して順序なし
&&論理短絡
||論理短絡
?:三項演算子右結合
= -= += = *= %= ^= ^^= ~= <<= >>= >>>=代入演算子右結合
=>ラムダ定義実際の演算子ではない;表に2回出現する;この行は右への結合力を示すためのもの
,コンマ演算子コンマを区切り文字として使用する場合(パラメータリストなど)と混同しないようにしよう
..数値範囲実際の演算子ではない;特定のポイントで構文にハードコーディングされている
チェーン

章の冒頭の行を考えてみよう:

a = b + c * d
D

2項演算子*は2項演算子+よりも優先順位が高いため、また2項演算子+=よりも優先順位が高いため、この式は、括弧で囲んだ以下の式と同等として実行される。

a = (b + (c * d))    // まず*、次に+、そして=
D

別の例として、後置演算子.は単項演算子*よりも優先順位が高いため、次の式はまずオブジェクトoのメンバーptrにアクセスし、次にそれを逆参照する。

*o.ptr      // ← ポインタメンバーo.ptrを参照
*(o.ptr)    // ← 上記と同等
(*o).ptr    // ← 上記と同等ではない
D

一部の演算子は連鎖できない:

if (a > b == c) {      // ← コンパイルエラー
    // ...
}
D
エラー: 期待される')'の代わりに'=='が見つかった

プログラマーは、希望する実行順序を括弧で指定する必要がある:

if ((a > b) == c) {    // ← コンパイルできる
    // ...
}
D
結合性

2つの演算子が同じ優先順位を持つ場合、その結合性がどちらの演算子が先に実行されるかを決定する:左側の演算子か右側の演算子か。

ほとんどの演算子は左結合性であり、左側の演算子が先に実行される:

10 - 7 - 3;
(10 - 7) - 3;    // ← 上記と同等(== 0)
10 - (7 - 3);    // ← 上記と同等ではない(== 6)
D

一部の演算子は右結合性であり、右側の演算子が先に実行される:

4 ^^ 3 ^^ 2;
4 ^^ (3 ^^ 2);    // ← 上記と同等(== 262144)
(4 ^^ 3) ^^ 2;    // ← 上記と同等ではない(== 4096)
D
順序のない演算子グループ

ビット演算子と論理演算子の優先順位は言語で指定されていない:

if (a & b == c) {      // ← コンパイルエラー
    // ...
}
D
エラー: b == c は演算子&の隣にある場合は括弧で囲む必要がある

プログラマーは、希望する実行順序を括弧で指定する必要がある:

if ((a & b) == c) {    // ← コンパイルできる
    // ...
}
D
演算子の優先順位=>

=>は演算子ではないが、左側と右側の演算子との相互作用を指定するため、表に2回登場する。

l = a => a = 1;
D

上記の=>の両側に=演算子が存在する場合、=>は左側の=よりも優先されるため、プログラマーが以下の括弧を明示的に指定した場合と同様に、aより強く結合する:

l = (a => a = 1);
D

右側では、=>=よりも優先順位が低いため、右側のaは、以下の追加の括弧が指定されたかのように、=に強く結合する。

l = (a => (a = 1));
D

その結果、ラムダ式はa => aだけになるのではなく、残りの式a => a = 1も含まれる。これは、aが与えられた場合、a = 1を生成することを意味する。このラムダは、変数lに割り当てられる。

注釈:これは単なる例だ。そうでなければ、a = 1はラムダ式にとって意味のある本体ではない。そのパラメータaへの変更が失われたように見え、ラムダ式を呼び出した結果は常に1になってしまうからだ("失われたように見える"と言ったのは、aの型に対して代入演算子がオーバーロードされており、副作用があるかもしれないからだ)。

コンマ演算子

コンマ演算子は2項演算子である。まず左側の式を実行し、次に右側の式を実行する。両方の式の値は無視される。

int a = 1;
foo(), bar(), ++a;

assert(a == 2);
D

コンマ演算子は、ループの反復で複数の変数を変更する場合のforループで最もよく使われる。

for ({ int i; int j; } i < 10; ++i, ++j) {
    // ...
}
D