私はワンライナーが大好きなので (最後に説明するように、ワンライナーはあらゆる種類の奇妙なものに非常に役立ちます)、std::accumulate と C++11 ラムダを使用したソリューションを次に示します。
std::accumulate(alist.begin(), alist.end(), std::string(),
[](const std::string& a, const std::string& b) -> std::string {
return a + (a.length() > 0 ? "," : "") + b;
} )
この構文は、単純な文字列結合を行うためだけに、ストリーム操作の範囲外にあらゆる種類の奇妙なロジックを持ちたくないストリーム演算子で役立ちます。たとえば、ストリーム演算子 (std; を使用) を使用して文字列をフォーマットするメソッドからの次の return ステートメントを考えてみましょう。
return (dynamic_cast<ostringstream&>(ostringstream()
<< "List content: " << endl
<< std::accumulate(alist.begin(), alist.end(), std::string(),
[](const std::string& a, const std::string& b) -> std::string {
return a + (a.length() > 0 ? "," : "") + b;
} ) << endl
<< "Maybe some more stuff" << endl
)).str();
アップデート:
コメントで @plexando が指摘したように、配列が空の文字列で始まる場合、上記のコードは誤動作の影響を受けます。これは、「最初の実行」のチェックで、追加の文字が発生しなかった以前の実行が欠落しているためです。また、-すべての実行で "is first run" のチェックを実行するのは奇妙です (つまり、コードは最適化されていません)。
リストに少なくとも 1 つの要素があるという事実がわかっている場合、これらの問題の両方の解決策は簡単です。OTOH、リストに少なくとも1つの要素がないことがわかっている場合は、実行をさらに短縮できます。
結果のコードはそれほどきれいではないと思うので、正しい解決策としてここに追加しますが、上記の議論にはまだメリットがあると思います。
alist.empty() ? "" : /* leave early if there are no items in the list */
std::accumulate( /* otherwise, accumulate */
++alist.begin(), alist.end(), /* the range 2nd to after-last */
*alist.begin(), /* and start accumulating with the first item */
[](auto& a, auto& b) { return a + "," + b; });
ノート:
- 最初の要素への直接アクセスをサポートするコンテナーの場合は、代わりにそれ
alist[0]
を 3 番目の引数 (ベクトル) に使用することをお勧めします。
- コメントとチャットでの議論によると、ラムダはまだいくつかのコピーを行っています。これは、代わりにこの(あまりきれいではない)ラムダを使用することで最小限に抑えることができます。これにより、
[](auto&& a, auto&& b) -> auto& { a += ','; a += b; return a; })
(GCC 10で)パフォーマンスが10倍以上向上します。提案をしてくれた @Deduplicator に感謝します。私はまだここで何が起こっているのかを理解しようとしています。