コンマオペレーターの仕組み


175

C ++ではカンマ演算子はどのように機能しますか?

たとえば、私が行う場合:

a = b, c;  

最終的に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;

詳しくはこちらをご覧ください。stackoverflow.com/questions/12824378/...
マッシュコーディング

1
Cでのカンマ演算子 `、`の機能の重複の可能性 。それは一日であなたを倒しました。そしてlillqの答えはに関する質問への答えを提供しますa = (b, c);
jww 2015年

5
しかし、この場合、a = b, c = d;実際には意図したとおりに機能しa = b; c = d;ますか?
Bondolin

@NargothBond必ずしもそうではありません。もしb及びd使用(および変更)一般的状態、実行順序がされるまで定義されていないこと関数評価ですC++17
ニロニウム2018

回答:



129

C ++ではカンマ演算子がオーバーロードされる可能性があることに注意してください。したがって、実際の動作は、予想される動作とは大きく異なる場合があります。

例として、Boost.Spiritはコンマ演算子を巧妙に使用して、シンボルテーブルのリスト初期化子を実装しています。したがって、次の構文が可能で意味のあるものになります。

keywords = "and", "or", "not", "xor";

演算子の優先順位により、コードは(意図的に!)

(((keywords = "and"), "or"), "not"), "xor";

つまり、最初に呼び出された演算子はkeywords.operator =("and")、残りoperator,のが呼び出されるプロキシオブジェクトを返します。

keywords.operator =("and").operator ,("or").operator ,("not").operator ,("xor");

ええと、優先順位を変更することはできません。つまり、リストをかっこで囲む必要があります。
Jeff Burdges、2011年

18
@Jeff逆に。リストをかっこで囲んだ場合、コンパイラーは2つのの間のコンマ演算子char[]を検出するだけなので機能しません。これはオーバーロードできません。コードは意図的に最初にを呼び出し、operator=次にoperator,残りの各要素に対してを呼び出します。
Konrad Rudolph

125

コンマ演算子は、すべてのC / C ++演算子の中で最も優先順位が低くなっています。したがって、これは常に式にバインドする最後のものです。つまり、次のようになります。

a = b, c;

以下と同等です。

(a = b), c;

もう1つの興味深い事実は、コンマ演算子がシーケンスポイントを導入することです。つまり、次の式は

a+b, c(), d

3つの部分式(a + bc()およびd)が順番に評価されることが保証されています。副作用がある場合、これは重要です。通常、コンパイラーは、部分式を適切な順序で評価できます。たとえば、関数呼び出しでは:

someFunc(arg1, arg2, arg3)

引数は任意の順序で評価できます。関数呼び出しのコンマ演算子ではないことに注意してください。それらはセパレータです。


15
,このように優先順位が低いことを指摘する価値はありますが、それ自体よりも遅れています ;)...つまり、カンマas- 演算子はコンマas- セパレータよりも優先順位が低くなっています。したがって、単一の関数の引数、変数の割り当て、またはその他のカンマ区切りのリスト内でカンマとして演算子を使用する場合は、括弧を使用する必要があります。例:int a = 1, b = 2, weirdVariable = (++a, b), d = 4;
underscore_d

68

カンマ演算子:

  • 優先順位が最も低い
  • 左結合です

コンマ演算子のデフォルトバージョンは、すべてのタイプ(組み込みおよびカスタム)に対して定義されており、次のように機能します-与えられたexprA , exprB

  • exprA 評価される
  • の結果exprAは無視されます
  • exprB 評価される
  • の結果はexprB、式全体の結果として返されます

ほとんどの演算子では、コンパイラーは実行の順序を選択することができ、最終結果に影響しない場合は実行をスキップする必要があります(たとえばfalse && foo()への呼び出しをスキップしますfoo)。ただし、これはコンマ演算子には当てはまりません。上記の手順は常に発生します*

実際には、デフォルトのカンマ演算子はセミコロンとほぼ同じように機能します。違いは、セミコロンで区切られた2つの式が2つの別個のステートメントを形成するのに対し、コンマ区切りはすべてを1つの式として保持することです。これが、次のシナリオでコンマ演算子が使用されることがある理由です。

  • C構文には、ステートメントではなく単一のが必要です。例えばif( HERE )
  • C構文には、たとえばforループの初期化など、1つのステートメントが必要です。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)---カンマ区切りの引数リスト。実際には、xyに評価することができます任意の順序!
  • FOO(x,y) ---カンマ区切りのマクロ引数リスト
  • foo<a,b> ---コンマ区切りのテンプレート引数リスト
  • int foo(int a, int b) ---コンマ区切りのパラメーターリスト
  • Foo::Foo() : a(5), b(3) {} ---クラスコンストラクター内のコンマ区切りの初期化子リスト

*最適化を適用する場合、これは完全には当てはまりません。特定のコードが残りのコードにまったく影響を与えないことをコンパイラが認識すると、不要なステートメントが削除されます。

参考資料http : //en.wikipedia.org/wiki/Comma_o​​perator


operator ,がオーバーロードされている場合、関連付けの保証が失われることに注意してください(ちょうどの短絡プロパティが失われ、オーバーロードされている場合operator&&operator||同様)。
YoungJohn

カンマ演算子は、オーバーロードされているかどうかに関係なく、左結合です。式はa, b, c常に意味し(a, b), c、決して意味しませんa, (b, c)。要素が異なるタイプである場合、後者の解釈はコンパイルエラーにつながる可能性さえあります。引数の評価の順序は何ですか?それについてはわかりませんが、おそらくあなたは正しいです。コンマが左結合である場合でも、前にc評価される 可能性があり(a, b)ます。
CygnusX1 2016

1
クラスコンストラクター内のコンマ区切りの初期化リストに関するわずかなコメントで、リスト内の位置によって順序は決定されません。順序はクラスの宣言位置によって決まります。例えば前にstruct Foo { Foo() : a(5), b(3) {} int b; int a; }エバリュエーション。リストが次のような場合、これは重要です。bは5に設定されませんが、初期化されていないaの値です。コンパイラが警告する場合と警告しない場合があります。b(3)a(5)Foo() : a(5), b(a) {}
Jonathan Gawrych

最近、2つの浮動小数点数を持つコンマ演算子に遭遇しました。数値を評価して破棄する意味は何ですか?
アーロンフランケ2018年

誰も答えられないと思います。コンテキストでそれを示す必要があります。おそらく別の質問ですか?
CygnusX1 2018年

38

の値はaになりbますが、式の値はなりますc。つまり、

d = (a = b, c);

aはに等しくbdはに等しくなりcます。


19
ほぼ正しい。ステートメントには値がありませんが、式にはあります。その式の値はcです。
Leon Timmermans

代わりにこれが使用されるのはなぜa = b; d = c;ですか?
アーロンフランケ

これにより、人々が話している副作用について理解することができました。
靴紐



2

はいカンマ演算子は代入演算子よりも優先順位が低くなっています

#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;
}

Ouput:i = 1
ご存知のように、コンマ演算子は割り当てより優先順位が低いです。


では、2番目の例はi = 1;その行にあるのとどう違うのですか?
アーロンフランケ

-3

まず最初に:カンマは実際には演算子ではありません。コンパイラにとって、これは他のトークンとの関連で意味を持つ単なるトークンです。

これはどういう意味で、なぜわざわざ?

例1:

異なるコンテキストでの同じトークンの意味の違いを理解するために、次の例を見てみましょう。

class Example {
   Foo<int, char*> ContentA;
}

通常、C ++の初心者は、この式は/物事を比較するだろうことができると思うだろうが、それは、absolutly間違っているの意味<>および,使用の状況にdepentトークン。

上記の例の正しい解釈は、もちろんそれがテンプレートのインスタンス化であるということです。

例2:

ループの各反復後に実行する必要のある、複数の初期化変数または複数の式、あるいはその両方で通常のforループを作成する場合も、コンマを使用します。

for(a=5,b=0;a<42;a++,b--)
   ...

コンマの意味は、使用のコンテキストに依存します。ここでは、forコンストラクションのコンテキストです。

コンテキスト内のコンマは実際にはどういう意味ですか?

さらに複雑にするために(いつものようにC ++では)、コンマ演算子自体をオーバーロードすることができます(指摘してくれたKonrad Rudolphに感謝します)。

質問に戻るために、コード

a = b, c;

コンパイラのようなもの

(a = b), c;

ので、優先順位=トークン/オペレータは、優先度よりも高い,トークン。

これは次のようなコンテキストで解釈されます

a = b;
c;

(解釈はコンテキストに依存することに注意してください。ここでは、関数/メソッドの呼び出しでもテンプレートのインスタンス化でもありません。)


1
確かに、おそらく私は間違った用語を使用しました(レクサーの場合はトークンです)
Quonux

2
1つの作品としてオペレータ、(原文のまま)、コンマは確かに演算子です。
DragonLord 2013

2
与えられたコンマトークンがコンマ演算子として認識されるかどうか(たとえば、引数の区切り文字ではなく)を認識すること自体は難しい場合がありますが、この質問は特にコンマ演算子についてです。
CygnusX1 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.