以下のプログラムの出力はなぜそれが何であるのですか?
#include <iostream>
using namespace std;
int main(){
cout << "2+3 = " <<
cout << 2 + 3 << endl;
}
作り出す
2+3 = 15
期待の代わりに
2+3 = 5
この質問はすでに複数のクローズ/再開サイクルを行っています。
終了に投票する前に、この問題に関するこのメタディスカッションを検討してください。
以下のプログラムの出力はなぜそれが何であるのですか?
#include <iostream>
using namespace std;
int main(){
cout << "2+3 = " <<
cout << 2 + 3 << endl;
}
作り出す
2+3 = 15
期待の代わりに
2+3 = 5
この質問はすでに複数のクローズ/再開サイクルを行っています。
終了に投票する前に、この問題に関するこのメタディスカッションを検討してください。
回答:
意図的であろうと偶然で<<あろうと、最初の出力行の終わりにあります;。だからあなたは本質的に持っています
cout << "2+3 = "; // this, of course, prints "2+3 = "
cout << cout; // this prints "1"
cout << 2 + 3; // this prints "5"
cout << endl; // this finishes the line
質問はこれに要約されます:なぜcout << cout;印刷するの"1"ですか?
これは、おそらく驚くべきことに、微妙です。std::coutは、その基本クラスを介して、次のようにブールコンテキストで使用することを目的とstd::basic_iosした特定の型変換演算子を提供します。
while (cout) { PrintSomething(cout); }
これは、出力を失敗させるのが難しいため、かなり貧弱な例std::basic_iosですが、実際には入力ストリームと出力ストリームの両方の基本クラスであり、入力の場合は、はるかに理にかなっています。
int value;
while (cin >> value) { DoSomethingWith(value); }
(ストリームの終わり、またはストリーム文字が有効な整数を形成しないときにループから抜けます)。
現在、この変換演算子の正確な定義は、標準のC ++ 03バージョンとC ++ 11バージョンの間で変更されています。古いバージョンではoperator void*() const;(通常はとして実装されていましたreturn fail() ? NULL : this;)、新しいバージョンでは(通常はexplicit operator bool() const;単にとして実装されていますreturn !fail();)。どちらの宣言もブール値のコンテキストでは問題なく機能しますが、そのようなコンテキストの外で(誤って)使用すると、動作が異なります。
特に、C ++ 03ルールでcout << coutは、cout << cout.operator void*()アドレスとして解釈され、出力されます。C ++ 11ルールの下cout << coutでは、演算子は宣言されexplicitているため、暗黙的な変換に参加できないため、まったくコンパイルしないでください。実際、それが変更の主な動機であり、無意味なコードがコンパイルされないようにしています。いずれかの標準に準拠するコンパイラは、印刷するプログラムを生成しません"1"。
どうやら、特定のC ++実装では、コンパイラーとライブラリーを混合して一致させ、不適合な結果を生成することができます(@StephanLechnerを引用:「1を生成するxcodeの設定と、アドレスを生成する別の設定を見つけました:言語方言c ++ 98を「標準ライブラリlibc ++(c ++ 11をサポートするLLVM標準ライブラリ)」と組み合わせると1になり、c ++ 98をlibstdc(gnu c ++標準ライブラリ)と組み合わせるとアドレスが作成されます; ")。explicit変換演算子(C ++ 11の新機能)を認識しないC ++ 03スタイルのコンパイラと、変換をとして定義するC ++ 11スタイルのライブラリを組み合わせることができますoperator bool()。このような組み合わせを使用すると、cout << coutをと解釈することが可能になりcout << cout.operator bool()、これは単純cout << trueに印刷され"1"ます。
Igorが言うように、これはC ++ 11ライブラリで得られstd::basic_iosます。のoperator bool代わりにを持っていますが、operator void*どういうわけか宣言されていない(または扱われていない)explicit。正しい宣言については、こちらをご覧ください。
たとえば、適合するC ++ 11コンパイラは同じ結果を返します
#include <iostream>
using namespace std;
int main() {
cout << "2+3 = " <<
static_cast<bool>(cout) << 2 + 3 << endl;
}
しかし、あなたの場合、static_cast<bool>暗黙の変換として(間違って)許可されています。
編集:これは通常の動作ではないため、プラットフォームやコンパイラのバージョンなどを知っておくと便利です。
編集2:参考までに、コードは通常次のように書かれます
cout << "2+3 = "
<< 2 + 3 << endl;
またはとして
cout << "2+3 = ";
cout << 2 + 3 << endl;
バグを露呈した2つのスタイルが混在しています。
予期しない出力の理由はタイプミスです。あなたはおそらく意味した
cout << "2+3 = "
<< 2 + 3 << endl;
予期される出力を持つ文字列を無視すると、次のようになります。
cout << cout;
C ++ 11以降、これは形式が正しくありません。std::coutは暗黙のうちにstd::basic_ostream<char>::operator<<(またはメンバー以外のオーバーロード)が受け入れるものに変換できません。したがって、標準準拠のコンパイラは、少なくともこれを行うことについて警告する必要があります。私のコンパイラはプログラムのコンパイルを拒否しました。
std::coutはに変換可能boolであり、ストリーム入力演算子のboolオーバーロードは、1の観測された出力を持ちます。ただし、そのオーバーロードは明示的であるため、暗黙的な変換は許可されません。コンパイラ/標準ライブラリの実装が標準に厳密に準拠していないようです。
C ++ 11より前の標準では、これは正しい形式です。当時は、ストリーム入力演算子のオーバーロードを持つstd::cout暗黙的な変換演算子がvoid*ありました。ただし、その出力は異なります。std::coutオブジェクトのメモリアドレスを出力します。
投稿されたコードは、C ++ 11(またはそれ以降の準拠コンパイラ)用にコンパイルするべきではありませんが、C ++ 11以前の実装では警告なしでコンパイルする必要があります。
違いは、C ++ 11がストリームをブール値に明示的に変換したことです。
C.2.15節27:入出力ライブラリ[diff.cpp03.input.output] 27.7.2.1.3、27.7.3.4、27.5.5.4
変更:既存のブール変換演算子での明示的使用の指定
根拠:意図を明確にし、回避策を回避します。
元の機能への影響:暗黙的なブール変換に依存する有効なC ++ 2003コードは、この国際標準でコンパイルできません。このような変換は、次の条件で発生します。
- bool型の引数を取る関数に値を渡す。
...
ostream演算子<<はboolパラメーターで定義されています。boolへの変換が存在する(明示的ではなかった)cout << coutため、C ++ 11より前のバージョンに変換されたため、cout << true1が生成されました。
C.2.15によると、これはC ++ 11以降ではコンパイルできません。
boolC ++ 03には存在する変換はありませんstd::basic_ios::operator void*()が、条件式またはループの制御式として意味があるものはあります。
この方法でコードを簡単にデバッグできます。使用時にcout出力はバッファリングされるため、次のように分析できます。
の最初の出現がcoutバッファを表し、演算子がバッファ<<の最後に追加することを想像してください。<<あなたの場合、演算子の結果は出力ストリームcoutです。あなたはから始めます:
cout << "2+3 = " << cout << 2 + 3 << endl;
上記のルールを適用すると、次のような一連のアクションが得られます。
buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);
前に言ったように、結果buffer.append()はバッファです。最初はバッファが空で、次のステートメントを処理する必要があります。
ステートメント: buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);
バッファ: empty
まずbuffer.append("2+3 = ")、与えられた文字列を直接バッファに入れてになりbufferます。これで、状態は次のようになります。
ステートメント: buffer.append(cout).append(2 + 3).append(endl);
バッファ: 2+3 =
その後、ステートメントの分析を続けcout、引数としてバッファーの最後に追加することに出会います。cout扱われ1ますが追加されますので1、あなたのバッファの最後に。今、あなたはこの状態にあります:
ステートメント: buffer.append(2 + 3).append(endl);
バッファ: 2+3 = 1
次にバッファにあるのは2 + 3、加算は出力演算子よりも優先順位が高いため、最初にこれらの2つの数値を加算してから、結果をバッファに格納します。その後、あなたは得る:
ステートメント: buffer.append(endl);
バッファ: 2+3 = 15
最後endlに、バッファーの最後にの値を追加すると、次のようになります。
ステートメント:
バッファ: 2+3 = 15\n
このプロセスの後、バッファーからの文字がバッファーから標準出力に1つずつ出力されます。したがって、コードの結果は2+3 = 15です。あなたはこれを見ている場合は、追加取得1からcout印刷してみました。<< coutステートメントから削除すると、目的の出力が得られます。
cout << cout農産物が生産さ1れるのか」ということになると思います。、そしてあなたはそれが挿入演算子の連鎖についての議論の最中にそれを行うと主張したところです。
;最初の出力行の終わりではなく、セミコロンが必要です<<。自分が印刷していると思うものを印刷していない。あなたはやっていますcout << cout、それは印刷します1(それは使用するcout.operator bool()と思います)。次に5(から2+3)がすぐ後に続き、15のように見えます。