C言語の条件演算子に関する雑記
条件演算子の中央オペランド(第2オペランド)がconditional-expression
でない理由
C11の規格(N1570)を見ると、条件演算子の文法はP467 (6.5.15)にて、以下のように記されている。 (文法はBNFで書き直したので、原文通りではない。)
で、何故第2オペランドがconditional-expression
ではなくexpression
なのかという話である。
その答えは、第2オペランドに代入式を許可するためである。
1 ? a = 2 : 3;
という文を考えてみる。
Cの定義通り第2オペランドがexpression
であれば、a = 2
はexpression
として処理される。
しかし、第2オペランドをconditional-expression
としてしまうと、a = 2
はlogical-OR-expression
として解釈することはできず、代入式をlogical-OR-expression
とするには(a = 2)
のようにparenで括らなくてはならない。
よって、括弧なしで第2オペランドに生の代入式を使うためには、conditional-expression
でなくexpression
を使わなければいけないのである。
(ちなみに、assignment-expression
でも代入式は直接使えるようになるが、コンマがparenなしで使えなくなる。)
C++における中央オペランド(第2オペランド)の文法
C++14の規格(N3797)においては、条件演算子の文法はCとは若干異なり、以下のように定義されている。
Cと異なり、条件演算子の第3オペランドがconditional-expression
ではなくassignment-expression
となっている。
この違いの影響は、Wikipediaで例示されている、e = a ? b : c = d;
のような文において現れる。
Cのように第3オペランドがconditional-expression
であれば、e = ((a ? b : c) = d);
のように解釈され、条件演算子が代入の左辺となる。
これはC言語においてはエラーである。
(一応、大昔にはこれを可能にするgcc拡張も存在した。後述。)
一方、C++のように第3オペランドがassignment-expression
であれば、e = (a ? b : (c = d));
のように解釈される。
C++特有の機能を使っていないからと言って、拡張子を.cに直してしまうとコンパイルが通らなくなる例だ。
条件演算子による式を左辺値として使えるようにするgcc拡張
私の記憶では、gcc独自拡張によって、C言語でも条件演算子による式を左辺値として使えるはずだったのだが、wandbox(gcc-4.8.2)で試してみたところ、コンパイルが通らなかった。
不思議に思って調べてみた結果が以下の通りだ。
-
Lvalues - Using the GNU Compiler Collection (GCC)
- gcc-3.4.6 (gcc-3の最後のバージョン、リリース)における、当該拡張のドキュメント。
-
Lvalues: Using `?:', `,' and casts in lvalues.
- 条件演算子どころか、コンマやキャストによる式でさえも左辺値にできるという拡張である。
-
C Extensions - Using the GNU Compiler Collection (GCC)
- gcc-4.0.0 (gcc-4の最初のバージョン、リリース)における、C拡張一覧のドキュメント。
- Lvalues拡張が消えている。
どうやら10年以上前に廃止された拡張だったらしい。 コンパイラ拡張をできるだけ使わないように生きてきたとはいえ、今日に至るまで全く気付かなかったのは結構ショックだ。