私は現在、C++ Primerという本でC++を学んでいます。本の演習の1つは次のとおりです。
次の式が何をするのか説明してください:
someValue ? ++x, ++y : --x, --y
私たちは何を知っていますか?三項演算子はコンマ演算子よりも優先順位が高いことを知っています。二項演算子を使用すると、これを理解するのは非常に簡単でしたが、三項演算子を使用すると、少し苦労します。二項演算子を使用すると、「優先順位が高い」ということは、優先順位が高い式を括弧で囲むことができ、実行が変更されないことを意味します。
三項演算子の場合:
(someValue ? ++x, ++y : --x, --y)
コンパイラーがコードをどのようにグループ化するかを理解するのに私を助けない同じコードを効果的にもたらします。
ただし、C++コンパイラでテストした結果、式がコンパイルされることはわかっていますが、:
演算子がそれ自体で何を意味するかはわかりません。そのため、コンパイラは三項演算子を正しく解釈するようです。
次に、2つの方法でプログラムを実行しました。
#include <iostream>
int main()
{
bool someValue = true;
int x = 10, y = 10;
someValue ? ++x, ++y : --x, --y;
std::cout << x << " " << y << std::endl;
return 0;
}
結果:
11 10
一方、someValue = false
を使用すると、次のように出力されます。
9 9
なぜC++コンパイラは、三項演算子の真の分岐に対してx
のみをインクリメントするコードを生成し、三項の偽分岐に対してはx
とy
の両方をデクリメントするのですか?
次のように、実際のブランチの周りに括弧を付けることさえしました。
someValue ? (++x, ++y) : --x, --y;
それでも、11 10
になります。
@ Rakete が優れた答えで言ったように、これは注意が必要です。少し付け加えたいと思います。
三項演算子の形式は次のとおりです。
論理式または式
?
式:
割り当て式
したがって、次のマッピングがあります。
someValue
:論理式または式++x, ++y
:式--x, --y
または--x
のみですか?実際、それは--x
のみです。これは、代入式がコンマで区切られた2つの式として解析できないため(C++の文法規則に従って)、したがって--x, --y
は代入式として扱うことができないためです=。
その結果、三項(条件付き)式部分は次のようになります。
someValue?++x,++y:--x
読みやすくするために、++x,++y
が計算されることを考慮すると役立つ場合がありますas-if括弧で囲まれた(++x,++y)
; ?
と:
の間に含まれているものはすべてシーケンスされますafter条件付き。 (ポストの残りの部分については括弧で囲みます)。
次の順序で評価されます。
someValue?
(++x,++y)
または--x
(1のbool
resultに依存)この式は、カンマ演算子の左の部分式として扱われ、右の部分式は--y
になります。
(someValue?(++x,++y):--x), --y;
これは、左側がdiscarded-value expressionであることを意味します。つまり、確実に評価されますが、右側を評価してそれを返します。
では、someValue
がtrue
の場合はどうなりますか?
(someValue?(++x,++y):--x)
が実行され、x
およびy
がインクリメントされて11
および11
になります--y
。その後、y
をデクリメントして10
に戻します。振る舞いを「修正」するには、--x, --y
をカッコでグループ化してprimary expression whichis有効なエントリに変換しますassignment-expression *の場合:
someValue?++x,++y:(--x, --y);
* assignment-expressionをプライマリ式に接続する、かなり面白い長いチェーンです。
代入式 ---(で構成可能)-> 条件式-> 論理式->- 論理式-> 包括的または式-> 排他的または式-> および-式-> 等式式-> 関係式-> シフト式-> 加算式-> 乗法式-> 午後式-> キャスト式->- 単項式-> 後置式-> 一次式
うわー、それはトリッキーです。
コンパイラは、式を次のように認識します。
(someValue ? (++x, ++y) : --x), --y;
三項演算子には:
が必要です。そのコンテキストで単独で立つことはできませんが、その後、コンマが偽のケースに属する理由はありません。
これで、その出力を取得する理由がより理にかなっているかもしれません。 someValue
がtrueの場合、++x
、++y
、および--y
が実行されます。これは、y
を効果的に変更しませんが、x
に1を追加します。
someValue
がfalseの場合、--x
と--y
が実行され、両方とも1ずつ減分されます。
C++コンパイラが、三項演算子の真の分岐に対して
x
だけをインクリメントするコードを生成する理由
何が起こったのかを誤解した。真の分岐は、x
とy
の両方をインクリメントします。ただし、y
はその直後に無条件にデクリメントされます。
これがどのように起こるかです: 条件付き演算子はC++のコンマ演算子よりも優先順位が高い なので、コンパイラは次のように式を解析します。
(someValue ? ++x, ++y : --x), (--y);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^
コンマの後の「孤立した」--y
に注意してください。これは、最初にインクリメントされたy
をデクリメントすることにつながります。
次のように、実際のブランチの周りに括弧を付けることさえしました。
someValue ? (++x, ++y) : --x, --y;
あなたは正しい道を歩んでいましたが、間違ったブランチを括弧で囲んでいました。次のようにelse-branchを括弧で囲むことでこれを修正できます:
someValue ? ++x, ++y : (--x, --y);
あなたの問題は、三項式が実際にコンマよりも高い優先順位を持たないことです。実際、C++は単に優先順位によって正確に説明することはできません。C++は、3項演算子とコンマとの間の相互作用であり、分解されます。
a ? b++, c++ : d++
として扱われます:
a ? (b++, c++) : d++
(コンマは、優先順位が高いかのように動作します)。一方、
a ? b++ : c++, d++
として扱われます:
(a ? b++ : c++), d++
また、三項演算子が優先されます。
回答で見過ごされている点(コメントは触れていますが)は、2つの値のいずれかを変数に割り当てるためのショートカットとして、条件付き演算子が実際のコードで常に使用される(設計で意図されていますか)ことです。
したがって、より大きなコンテキストは次のようになります。
whatIreallyWanted = someValue ? ++x, ++y : --x, --y;
これは一見不合理なので、犯罪は多岐にわたります。