質問
C++ ではカンマ演算子はどのように機能しますか?
たとえば、次のようにすると:
a = b, c;
aは最終的にbまたはcと等しくなるのでしょうか?
(はい、これが簡単にテストできることはわかっています。誰かがすぐに答えを見つけられるように、ここに文書化するだけです。)
アップデート: この質問では、カンマ演算子を使用する際のニュアンスが明らかになりました。これを文書化するだけです:
a = b, c; // a is set to the value of b!
a = (b, c); // a is set to the value of c!
この質問は、実際にはコードのタイプミスからインスピレーションを得たものです。何を意図していたのか
a = b;
c = d;
になって
a = b, // <- Note comma typo!
c = d;
解決
それは次と等しいでしょう b
.
カンマ演算子は代入よりも優先順位が低くなります。
他のヒント
C++ ではカンマ演算子がオーバーロードされる可能性があることに注意してください。したがって、実際の動作は予想される動作とは大きく異なる場合があります。
例として、 ブースト・スピリット は、コンマ演算子を非常に巧みに使用して、シンボル テーブルのリスト初期化子を実装します。したがって、次の構文が可能になり、意味のあるものになります。
keywords = "and", "or", "not", "xor";
演算子の優先順位により、コードは (意図的に!) 以下と同一であることに注意してください。
(((keywords = "and"), "or"), "not"), "xor";
つまり、最初に呼び出される演算子は、 keywords.operator =("and")
これはプロキシ オブジェクトを返します。 operator,
が呼び出されます:
keywords.operator =("and").operator ,("or").operator ,("not").operator ,("xor");
カンマ演算子には、 最低 すべての C/C++ 演算子の優先順位。したがって、これは常に式にバインドされる最後のものになります。これは次のことを意味します。
a = b, c;
は以下と同等です:
(a = b), c;
もう 1 つの興味深い事実は、カンマ演算子によって シーケンスポイント. 。これは次の式を意味します:
a+b, c(), d
には 3 つの部分式 (a+b, c() そして d) 順番に評価されます。副作用がある場合、これは重要です。通常、コンパイラは、適切と判断される順序で部分式を評価できます。たとえば、関数呼び出しでは次のようになります。
someFunc(arg1, arg2, arg3)
引数は任意の順序で評価できます。関数呼び出し内のカンマは次のとおりであることに注意してください。 ない 演算子。それらは区切り文字です。
カンマ演算子:
- 優先順位が最も低い
- 左連想です
カンマ演算子のデフォルト バージョンはすべての型 (組み込みおよびカスタム) に対して定義されており、次のように動作します。 exprA , exprB
:
exprA
評価される- 結果として
exprA
無視される exprB
評価される- 結果として
exprB
式全体の結果として返されます
ほとんどの演算子では、コンパイラーは実行順序を選択することができ、最終結果に影響を与えない場合には実行をスキップすることさえ要求されます (例: false && foo()
への呼び出しをスキップします foo
)。ただし、これはカンマ演算子の場合には当てはまらず、上記の手順が常に発生します。*.
実際には、デフォルトのカンマ演算子はセミコロンとほぼ同じように機能します。違いは、セミコロンで区切られた 2 つの式が 2 つの別々のステートメントを形成するのに対し、カンマ区切りではすべてが 1 つの式として保持されることです。このため、次のシナリオでカンマ演算子が使用されることがあります。
- C 構文には単一の 表現, 、声明ではありません。例えばで
if( HERE )
- C 構文には 1 つのステートメントが必要ですが、それ以上は必要ありません。の初期化で
for
ループfor ( HERE ; ; )
- 中括弧をスキップして単一のステートメントを保持したい場合は、次のようにします。
if (foo) HERE ;
(本当に見苦しいのでやめてください!)
ステートメントが式ではない場合、セミコロンをカンマに置き換えることはできません。たとえば、次のようなものは禁止されています。
(foo, if (foo) bar)
(if
表現ではありません)- int x、int y (変数宣言は式ではありません)
あなたの場合、次のようになります。
a=b, c;
, 、 に相当a=b; c;
, 、 仮定してa
カンマ演算子をオーバーロードしないタイプです。a = b, c = d;
に相当a=b; c=d;
, 、 仮定してa
カンマ演算子をオーバーロードしないタイプです。
すべてのカンマが実際にはカンマ演算子であるわけではないことに注意してください。まったく異なる意味を持ついくつかのカンマ:
int a, b;
--- 変数宣言リストはカンマで区切られていますが、これらはカンマ演算子ではありませんint a=5, b=3;
--- これもカンマ区切りの変数宣言リストですfoo(x,y)
--- カンマ区切りの引数リスト。実際には、x
そしてy
で評価できます どれでも 注文!FOO(x,y)
--- カンマ区切りのマクロ引数リストfoo<a,b>
--- カンマ区切りのテンプレート引数リストint foo(int a, int b)
--- カンマ区切りのパラメータリストFoo::Foo() : a(5), b(3) {}
--- クラス コンストラクター内のカンマ区切りの初期化子リスト
* 最適化を適用する場合、これは完全には当てはまりません。コンパイラは、特定のコード部分が残りの部分にまったく影響を及ぼさないことを認識すると、不要なステートメントを削除します。
の値 a
になるだろう b
, 、しかし、の値は 表現 になるだろう c
. 。つまり、
d = (a = b, c);
aは次と等しいでしょう b
, 、 そして d
に等しいでしょう c
.
b の値は a に代入されます。cには何も起こりません
カンマ演算子の優先順位は代入演算子よりも低いため、a の値は b と等しくなります。
はい、カンマ演算子は代入演算子より優先順位が低くなります。
#include<stdio.h>
int main()
{
int i;
i = (1,2,3);
printf("i:%d\n",i);
return 0;
}
出力:i=3
カンマ演算子は常に右端の値を返すためです。
カンマ演算子と代入演算子の場合:
int main()
{
int i;
i = 1,2,3;
printf("i:%d\n",i);
return 0;
}
出力:i=1
ご存知のとおり、カンマ演算子は代入よりも優先順位が低くなります。
まず最初に: カンマは実際には演算子ではなく、コンパイラーにとっては意味を取得する単なるトークンです 文脈の中で 他のトークンと一緒に。
これは何を意味し、なぜわざわざ行うのでしょうか?
例 1:
異なるコンテキストにおける同じトークンの意味の違いを理解するために、次の例を見てみましょう。
class Example {
Foo<int, char*> ContentA;
}
通常、C++ 初心者は、この式で物事を比較できる、または比較できると考えますが、それは完全に間違っています。 <
, >
そして ,
トークンは使用のコンテキストによって異なります。
もちろん、上記の例の正しい解釈は、これがテンプレートのインスタンス化であるということです。
例 2:
通常、ループの各反復後に実行する必要がある複数の初期化変数や複数の式を含む for ループを作成する場合は、カンマも使用します。
for(a=5,b=0;a<42;a++,b--)
...
コンマの意味は使用のコンテキストによって異なります。ここではコンマのコンテキストを示します。 for
工事。
コンテキスト内のカンマは実際には何を意味するのでしょうか?
(C++ ではいつもそうであるように) さらに複雑にするために、カンマ演算子自体をオーバーロードすることができます (おかげで コンラート・ルドルフ それを指摘してくれて)。
質問に戻りますが、コードは
a = b, c;
コンパイラにとって次のような意味です
(a = b), c;
なぜなら 優先度 の =
トークン/演算子は、 ,
トークン。
そしてこれは次のようなコンテキストで解釈されます
a = b;
c;
(解釈はコンテキストに依存することに注意してください。ここでは関数/メソッドの呼び出しでも、テンプレートのインスタンス化でもありません。)