2016-04-29 3 views
3

コンソールに何かを出力するのにstd::coutを使用すると、やや奇妙な動作に気付きました。
私はstring& toUpper(std::string &str)string& toLower(std::string &str)という2つの関数を書いています。これらの関数は、後で呼び出されるものとまったく同じです。文字列をすべて大文字またはすべて小文字に変換します。奇妙な行為

#include <string> 
using namespace std; 

string& toLower(string &str) 
{ 
    for(char &c : str) 
     c = tolower(c); 

    return str; 
} 

string& toUpper(string &str) 
{ 
    for(auto &c : str) 
     c = toupper(c); 

    return str; 
} 

ここでは、両方の機能を独立してテストしましたが、期待通りに動作しています。私は出力が

hello world 
HELLO WORLD 

ことが期待

string str = "Hello World"; 
cout << toLower(str) << endl << toUpper(str) << endl; 

ではなく、私はちょうど私がいるのでprintfを使用して同じことをテストし

hello world 
hello world 

ました:それから私はcoutコールでそれらの両方をチェーンcoutのやり方に特有のものかもしれないと思ったが、私は同じ結果を得たので、自分のコードに何か問題があると思う。
私のコードの問題点は何ですか?

+2

を参照するのではなく、コピーを返しますし、それは私がそのコピーが働くだろう知っているが、私は実際の文字列のインプレースを操作したい – OMGtechy

+0

をうまくいきます。 – TorbenJ

+1

同じ文字列を2回操作しています。 Coutはバッファリングされているので、私はそれが問題を引き起こしていると推測しています。 –

答えて

4

operator<<への呼び出しは、左から右に順番に行う必要がありますが、C++標準では、ステートメント内のサブ式の評価順序が指定されていません。

コンパイラは、これらの結果のいずれかが有効になるように、サブ式を評価するために決定することができます:

auto&& arg1 = toLower(str); 
auto&& arg2 = toUpper(str); 
cout << arg1 << endl << arg2 << endl; 

または:

auto&& arg1 = toUpper(str); 
auto&& arg2 = toLower(str); 
cout << arg2 << endl << arg1 << endl; 

または:

auto&& arg1 = toUpper(str); 
auto&& arg2 = (cout << arg1); 
auto&& arg3 = toUpper(str); 
arg2 << endl << arg3 << endl; 

または他のいくつかの可能性。これらの3つの可能なシーケンスのうち、最後のものだけがあなたが期待する結果を生み出します。最初のケースでは "HELLO WORLD"が2回印刷され、2番目のケースではコンパイラーの結果が得られます。すべてがC++標準に基づく有効な結果です。

+0

詳細な説明と背景情報をありがとう。確かに私は第3の解決策が起こることを期待していました。この状況に対して定義された動作がないことはあまりにも悪いことです。 – TorbenJ

0

問題は「関数を呼び出すとき」であり、参照がどのように処理されているかです。それはバッファリングされたストリームなので、それはおそらくそれらを順不同で呼んでいるようです。返された文字列の参照を削除すると(各関数に新しい一意の文字列が返されるので)、コードは正常に動作します。

void toLower(string &str) 
{ 
    for(char &c : str) 
     c = tolower(c); 

    return str; 
} 

void toUpper(string &str) 
{ 
    for(auto &c : str) 
     c = toupper(c); 
} 
+3

マイナス1 - 文字列を変更した後でコピーを返します。それは意味をなさない。 – Ven

+2

バッファリングとはまったく関係がありません。文字列を変更する関数呼び出しは、ストリームへの書き込みが行われる前に評価されます。 –

4

式の評価内で変数(文字列)を変更しており、その評価中に特定のポイントで使用されています。あなたが見つけたように、あなたはそれに頼ることはできません。

解決策の1つは、異なる文字列を使用することです。別の表現解散することになります。

cout << toLower(str) << endl << str << endl;//str = HELLO WORLD 

第二ステップはTOLOWERを評価:

cout << toLower(str) << endl << toUpper(str) << endl; //str = Hello World 

第一ステップは、TOUPPER評価:

cout << toLower(str) << endl; 
cout << toUpper(str) << endl; 
+1

だけでなく、あなたが*それを*頼ることができない、それは*はっきり*未定義の動作です。ちょうど 'i = i ++'のようです。 – Ven

+0

@Ven:それは未定義することが何を意味するのか、特定の動作に依存することができないではありませんか。 –

+1

nope。評価順序は未定義です - 'a(b()、c())'では、 'c()'の前で実行されている 'b()'に依存することはできませんが、未定義の動作はありません。また、暗黙的に定義された動作でもあります。 – Ven

4

その方法をC++には、あなたの文を解析し

cout << str << endl << str << endl;//str = hello world 

第3ステップCout:

cout <<"hello world\nhello world\n"; 

coutがこの結果を生成する理由は、印刷前に同じ文字列を変更しているためです。これを修正するには、参照の代わりにコピーを使用してください。