web-dev-qa-db-ja.com

std :: stringstreamを初期化する方法?

文字列を整数で連結する必要があります。そのために、私はstringstreamを次のように使用しています。

int numPeople = 10;
stringstream ss;
ss << "Number of people is " << numPeople;

そしてそれはうまくいった。しかし、私は以下の方法でそれをやろうとしました:

int numPeople = 10;
stringstream ss << "Number of people is " << numPeople;

そして、私は次のエラーを受け取りました: "'<<'トークンの前に期待される初期化子"

このエラーが発生したのはなぜですか?宣言と同時にstringstream値を割り当てることができないのはなぜですか?

20
Dragons_Lair5
stringstream ss << "Number of people is " << numPeople;

stringstreamの値を宣言と同時に割り当てることができないのはなぜですか?

これはこれがうまくいくことを望んでいるのと似ています:

int x + 3 + 9;

問題

オブジェクトの定義と値の指定の両方を行う場合、C++ではコンストラクターを呼び出すことができます。これには、コンマで区切られた式のリストが必要です。便宜上、Type variable = value;表記はType variable(value);を呼び出すために省略されていますが、単一の値に対してのみ機能します。

intの場合、コードを簡単に修正できます。

int x = 3 + 9;

...「3 + 9」を最初に個別に評価して、xに保存する適切な値を与えることができるため、機能します。 intsの演算子+に対するコンパイラの動作は、私たちが望むことを行いますint結果を生成して、xに格納します。しかし、stringstreamにそれを試してみると...

stringstream ss = "Number of people is " << numPeople;  // BROKEN

..."Number of people is " << numPeopleを最初に評価する必要がありますが、これは違法であるため、機能しません。「error C2296: '<<' : illegal, left operand has type 'const char [20]'」のようなエラーが発生します。stringstreamコンストラクターに有用な値が提供されません。問題は、適用する<<のオーバーロードにはostream&型の左側の引数が必要であるため、コンパイラーがビット単位のシフト演算子を適用しようとしていることです。これは数値に対してのみ意味があります。 C++では、=の右側で評価される値が、最終的に結果の値で行われる割り当てとは無関係に評価される必要があり、その時点で、構築される変数のタイプは、式の評価が試行される方法とは関係ありません。割り当てられた。

解決策

stringstreamのコンストラクターを呼び出すには、stringstreamで必要な右側の値を組み合わせる必要があるため、ここでは少し鶏と卵の問題が発生しますが、そのためにはstringstreamが必要です。実際には、一時的なstringstreamを使用してそれを引き出すことができます。

static_cast<std::ostringstream&&>(std::ostringstream() << "Number of people is " << numPeople)

残念ながら、operator<<オーバーロードはstringstream基本クラスへの参照を介してostreamsを処理し、ostream&を返すので、キャストは必要です。手動でstringstream型にキャストバックして、std::stringstream moveコンストラクターを呼び出すことができます...

完全なワンライナー構造は、次のようになります...

std::stringstream ss(static_cast<std::ostringstream&&>(std::ostringstream() << "Number of people is " << numPeople));

...しかしそれは熟考するにはあまりにも恐ろしいです。

ソリューションを(間違いなく)それほど目立たなくする

あなたの感性によっては、マクロが助けになるか悪いと感じるかもしれません...

#define OSS(VALUES) \
    static_cast<std::ostringstream&&>(std::ostringstream() << VALUES)

std::stringstream ss(OSS("Number of people is " << numPeople));

FWIW、マクロを使用して文字列を作成することもできます...

std::string s(OSS("Number of people is " << numPeople).str()); 

(間違いなく)より良い実践

stringstreamを作成するだけです-オプションでコンストラクタに単一のstringを提供し、2番目のステートメントでoperator<<を使用します。

std::stringstream ss;
ss << "Number of people is " << numPeople;

これは非常に読みやすいです。移動構築では、最適化後、2つのステートメントを優先するパフォーマンス上の理由はおそらくありません。

代替

C++ 11で導入された to_string() オーバーロードは、stringと連結または連結する整数値が1つまたは2つある場合に便利です。

std::string s = "Number of people is " + std::to_string(numPeople);

ただし、これは非効率的かもしれません(必要に応じてコンパイラの最適化機能を確認してください)。各std::to_string()は、独立したstd::stringインスタンスに動的にバッファを割り当てる可能性が高く、個々の連結にテキストの追加コピーが含まれる可能性があります、および元の動的に割り当てられたバッファを拡大する必要がある場合、それらの一時的なstd::stringsのほとんどは、破棄中に割り当て解除に時間がかかります。

C++ 03の方が悪かった

C++ 03にはmoveコンストラクターがなかったため、テンポラリで std::ostringstream::str() メンバー関数を使用して、名前付きのstd::stringの追加のディープコピーを取得する必要がありました。 stringsteam...

stringstream ss(static_cast<std::ostringstream&>(std::ostringstream() << "Number of people is " << numPeople).str());

このC++ 03コードでは、動的なメモリ割り当てとコンテンツのコピーが重複する可能性があるため、構築に続くストリーミングの方が適しています。

31
Tony Delroy

Stringbufは、値を追加する前に初期化する必要があります。 stringstreamはオブジェクトです。

行うことにより:

std::stringstream ss;

基本的に、アプリケーションで追加される値を処理するためのスペースを割り当てることができます。

0
dotslashb