次の例では:
cout<<"\n"[a==N];
[]
オプションはcout
で使用しますが、a
の値がN
と等しい場合は改行を出力しません。
[]オプションがcoutで何をするかについての手がかりはありません
これは実際にはcout
オプションではなく、"\n"
が 文字列リテラル であることが発生しています。文字列リテラルのタイプはn const charの配列です。[]
は、この場合は以下を含む文字の配列へのインデックスです。
\n\0
注\0
は、すべての文字列リテラルに追加されます。
==
演算子の結果はtrueまたはfalseになるため、インデックスは次のようになります。
0
falseの場合、a
がN
と等しくない場合は\n
になります。1
trueの場合、a
がN
と等しい場合\0
これはかなり不可解で、単純なif
に置き換えることができます。
参考までに、C++ 14標準(明度はドラフトが実際の標準に一致することを確認しました)に最も近いドラフトは N3936 セクション2.14.5
文字列リテラル[Lex.string]と言います(強調鉱山):
文字列リテラルのタイプは「n const charの配列」であり、nは以下で定義されている文字列のサイズであり、静的ストレージ期間(3.7)を持ちます。
そして:
必要な連結の後、変換フェーズ7(2.2)では、すべての文字列リテラルに '\ 0'が追加されるため、文字列をスキャンするプログラムはその終わり。
セクション4.5
[conv.prom]言う:
Bool型のprvalueはint型のprvalueに変換でき、falseはゼロになり、trueは1になります。
Null文字をテキストストリームに書き込む
Null文字(\0
)をテキストストリームに書き込むことは未定義の動作であるという主張が行われました。
これが合理的な結論であると言える限り、cout
は、27.4.2
[narrow.stream.objects]からわかるように、Cストリームに関して定義されます。
オブジェクトcoutは、<cstdio>(27.9.2)で宣言された、オブジェクトstdoutに関連付けられたストリームバッファーへの出力を制御します。
セクション7.21.2
のC11ドラフト標準ストリームはこう言います:
[...]テキストストリームから読み込まれたデータは、以下の場合にのみ、そのストリームに以前に書き込まれたデータと必ず比較されます。データが印刷文字と制御文字の水平タブと改行のみで構成されている。
文字の印刷は7.4
でカバーされています文字処理<ctype.h>:
[...]制御文字という用語は、文字を印刷しないロケール固有の文字セットのメンバーを指します。199)すべての文字と数字は印刷文字です。
脚注199
と言って:
7ビットUS ASCII文字セットを使用する実装では、印刷文字は、値が0x20(スペース)から0x7E(チルド)までの文字であり、制御文字は、値が0(NUL)から0x1F(US)、および文字0x7F(DEL)。
そして最後に、ヌル文字を送信した結果が指定されていないことがわかります。これは、セクション4
の適合性からの未定義の動作であることがわかります。
[...]未定義の動作は、この「国際規格」では、「「未定義の動作」」という言葉によって、または動作の明示的な定義の省略によって、別の方法で示されています。[...]
C99の根拠 も確認できます。
テキストストリームI/Oで保持する必要がある文字のセットは、Cプログラムの作成に必要な文字です。その意図は、Cトランスレータが最大限に移植可能な方法で書かれることを規格が許可するべきであるということです。この目的では、バックスペースなどの制御文字は必要ないため、テキストストリームでの処理は必須ではありません。
_cout<<"\n"[a==N];
_[]オプションがcoutで何をするかについての手がかりはありません
C++演算子の優先順位テーブル では、_operator []
_は_operator <<
_よりも緊密にバインドされるため、コードは次のようになります。
_cout << ("\n"[a==N]); // or cout.operator <<("\n"[a==N]);
_
または言い換えると、_operator []
_はcout
を直接使用して何も行いません。文字列リテラル_"\n"
_のインデックス作成にのみ使用されます
たとえば、for(int i = 0; i < 3; ++i) std::cout << "abcdef"[i] << std::endl;
は、画面上の連続する行に文字a、b、cを出力します。
_C++
_の 文字列リテラル は常にnull文字で終了するため(_'\0'
_、_L'\0'
_、char16_t()
など)、文字列リテラル_"\n"
_は、文字__const char[2]
_および_'\n'
_を保持する_'\0'
_です。
メモリレイアウトでは、次のようになります。
_+--------+--------+
| '\n' | '\0' |
+--------+--------+
0 1 <-- Offset
false true <-- Result of condition (a == n)
a != n a == n <-- Case
_
したがって、_a == N
_がtrue(1に昇格)の場合、式_"\n"[a == N]
_は_'\0'
_になり、結果がfalseの場合は_'\n'
_になります。
機能的には以下と似ています(同じではありません)。
_char anonymous[] = "\n";
int index;
if (a == N) index = 1;
else index = 0;
cout << anonymous[index];
_
valueof _"\n"[a==N]
_は_'\n'
_または_'\0'
_です
typeof _"\n"[a==N]
_ is _const char
_
何も印刷しない場合(プラットフォームと目的によっては_'\0'
_の印刷とは異なる場合があります)、次のコード行を優先します。
_if(a != N) cout << '\n';
_
たとえストリームに_'\0'
_または_'\n'
_のいずれかを書き込む場合でも、次のような読みやすいコードを使用してください。
_cout << (a == N ? '\0' : '\n');
_
それはおそらく奇妙な書き方として意図されている
if ( a != N ) {
cout<<"\n";
}
[]
演算子は、配列から要素を選択します。文字列"\n"
は、実際には2文字の配列です:改行'\n'
と文字列ターミネーター'\0'
。したがって、cout<<"\n"[a==N]
は'\n'
文字または'\0'
文字のいずれかを出力します。
問題は、テキストモードでI/Oストリームに'\0'
文字を送信することが許可されていないことです。そのコードの作成者は、何も起こらないように見えることに気付いた可能性があるため、cout<<'\0'
は何もしない安全な方法であると想定しました。
CおよびC++では、未定義の動作の概念のため、これは非常に悪い仮定です。プログラムが、標準または特定のプラットフォームでは、何が起こる可能性があります。この場合、かなり可能性の高い結果は、ストリームが完全に機能しなくなることです。これ以上cout
への出力はまったく表示されなくなります。
要約すると、効果は、
「
a
がN
と等しくない場合は改行を出力します。それ以外の場合は、わかりません。クラッシュか何かです。」
…そして道徳は、物事をそれほど秘密に書いてはいけないということです。
cout
のオプションではなく、"\n"
の配列インデックス
配列インデックス[a==N]
は[0]または[1]に評価され、改行とヌル文字を含む"\n"
で表される文字配列にインデックスを付けます。
ただし、nulをiostreamに渡すと結果が定義されないため、文字列を渡す方が適切です。
cout << &("\n"[a==N]) ;
ただし、どちらの場合のコードも特にお勧めできず、難読化する以外に特別な目的はありません。それを良い習慣の例と考えないでください。ほとんどの場合、以下が推奨されます。
cout << (a != N ? "\n" : "") ;
あるいは単に:
if( a != N ) cout << `\n` ;
次の各行は、まったく同じ出力を生成します。
_cout << "\n"[a==N]; // Never do this.
cout << (a==N)["\n"]; // Or this.
cout << *((a==N)+"\n"); // Or this.
cout << *("\n"+(a==N)); // Or this.
_
他の回答で指定されているように、これは_std::cout
_とは関係ありません。代わりに
CおよびC++でのプリミティブ(オーバーロードされていない)添え字演算子の実装方法。
両方の言語で、array
がCスタイルのプリミティブ配列である場合、_array[42]
_は*(array+42)
の構文糖です。さらに悪いことに、_array+42
_と_42+array
_の間に違いはありません。これは興味深い難読化につながります。コードを完全に難読化することが目的の場合は、_42[array]
_ではなく_array[42]
_を使用してください。言うまでもなく、理解しやすく保守しやすいコードを書くことが目標である場合、_42[array]
_を書くのはひどい考えです。
ブール値を整数に変換する方法。
_a[b]
_という形式の式を考えると、a
またはb
のいずれかがポインター式であり、もう一方がでなければなりません。もう一方は整数式でなければなりません。 _"\n"[a==N]
_という式が与えられた場合、_"\n"
_はその式のポインター部分を表し、_a==N
_は式の整数部分を表します。ここで、_a==N
_はfalse
またはtrue
に評価されるブール式です。整数昇格規則では、整数への昇格時にfalse
が0になり、true
が1になるように指定されています。
文字列リテラルがどのようにポインタに分解されるか。
ポインタが必要な場合、CおよびC++の配列は、配列の最初の要素を指すポインタにすぐに分解されます。
文字列リテラルの実装方法。
すべてのCスタイルの文字列リテラルには、ヌル文字_'\0'
_が追加されます。これは、_"\n"
_の内部表現が配列_{'\n', '\0'}
_であることを意味します。
上記を前提として、_a==N
_がfalse
に評価されるとします。この場合、動作はすべてのシステムにわたって明確に定義されています。改行されます。一方、_a==N
_がtrue
と評価される場合、動作はシステムに大きく依存します。質問に対する回答へのコメントに基づいて、Windowsはそれを好きではありません。 _std::cout
_がターミナルウィンドウにパイプされるUnixライクなシステムでは、動作はどちらかと言えば無害です。何も起こりません。
そのようなコードを記述できるからといって、そうする必要があるわけではありません。そのようなコードは絶対に書かないでください。