std::string
からstd::vector<std::string>
を構築したいと思います。
std::stringsteam
を使用できますが、もっと短い方法があると想像してください。
std::string string_from_vector(const std::vector<std::string> &pieces) {
std::stringstream ss;
for(std::vector<std::string>::const_iterator itr = pieces.begin();
itr != pieces.end();
++itr) {
ss << *itr;
}
return ss.str();
}
他にどのようにこれを行うことができますか?
std::string s;
for (std::vector<std::string>::const_iterator i = v.begin(); i != v.end(); ++i)
s += *i;
return s;
std::string s;
std::for_each(v.begin(), v.end(), [&](const std::string &piece){ s += piece; });
return s;
std::string s;
for (const auto &piece : v) s += piece;
return s;
文字列の連結にstd::accumulate
を使用しないでください、これは古典的な 画家のアルゴリズムのシュミエル であり、通常の使用例よりもさらに悪いCのstrcat
。C++ 11移動セマンティクスがない場合、ベクトルの各要素に対してアキュムレーターの2つの不要なコピーが発生します。移動セマンティクスを使用しても、各要素ごとにアキュムレーターの不要なコピーが1つ発生します。
上記の3つの例は、O(n)です。
std::accumulate
は、文字列の場合、O(n²)です。
カスタムファンクタを提供することで、文字列に
std::accumulate
O(n))を作成できます。std::string s = std::accumulate(v.begin(), v.end(), std::string{}, [](std::string &s, const std::string &piece) -> decltype(auto) { return s += piece; });
s
は非constへの参照である必要があり、ラムダの戻り値の型は参照である必要があり(したがってdecltype(auto)
)、本体は+=
ではなく+
を使用する必要があります。
C++ 20になる予定の現在のドラフトでは、アキュムレータに追加するときにstd::accumulate
を使用するためにstd::move
の定義が altered になっているため、C++ 20以降ではaccumulate
は、文字列ではO(n)になり、ワンライナーとして使用できます。
std::string s = std::accumulate(v.begin(), v.end(), std::string{});
_<numeric>
_ヘッダーから std::accumulate()
標準関数を使用できます(string
sに対して_operator +
_のオーバーロードが定義されているため機能します) 2つの引数の連結を返します):
_#include <vector>
#include <string>
#include <numeric>
#include <iostream>
int main()
{
std::vector<std::string> v{"Hello, ", " Cruel ", "World!"};
std::string s;
s = accumulate(begin(v), end(v), s);
std::cout << s; // Will print "Hello, Cruel World!"
}
_
または、より効率的で小さなfor
サイクルを使用することもできます。
_#include <vector>
#include <string>
#include <iostream>
int main()
{
std::vector<std::string> v{"Hello, ", "Cruel ", "World!"};
std::string result;
for (auto const& s : v) { result += s; }
std::cout << result; // Will print "Hello, Cruel World!"
}
_
演算子+を使用してそれらを一緒に追加しないのはなぜですか?
std::string string_from_vector(const std::vector<std::string> &pieces) {
return std::accumulate(pieces.begin(), pieces.end(), std::string(""));
}
std :: accumulateは、デフォルトでフードの下にstd :: plusを使用します。C++では、std :: stringに対して演算子+がオーバーロードされるため、2つの文字列を追加することは連結です。
私の個人的な選択は、 Oktalist's answer のように、範囲ベースのforループです。
BoostはNiceソリューションも提供します:
_#include <boost/algorithm/string/join.hpp>
#include <iostream>
#include <vector>
int main() {
std::vector<std::string> v{"first", "second"};
std::string joined = boost::algorithm::join(v, ", ");
std::cout << joined << std::endl;
}
_
これは印刷します:
第一、第二
いずれにせよ、std::accumulate()
アプローチは、そのアルゴリズムの誤用に近づいています(複雑さの問題に関係なく)。
パーティーに少し遅れましたが、初期化リストを使用できるという事実が気に入りました。
std::string join(std::initializer_list<std::string> i)
{
std::vector<std::string> v(i);
std::string res;
for (const auto &s: v) res += s;
return res;
}
次に、単純に呼び出すことができます(Pythonスタイル):
join({"Hello", "World", "1"})
Google Abseilには、必要なことを行うabsl :: StrJoin関数があります。
header ファイルの例。セパレータは""
// std::vector<std::string> v = {"foo", "bar", "baz"};
// std::string s = absl::StrJoin(v, "-");
// EXPECT_EQ("foo-bar-baz", s);
末尾のスペースが必要ない場合は、<numeric>
で定義されているaccumulate
をカスタム結合ラムダとともに使用します。
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
int main() {
vector<string> v;
string s;
v.Push_back(string("fee"));
v.Push_back(string("fi"));
v.Push_back(string("foe"));
v.Push_back(string("fum"));
s = accumulate(begin(v), end(v), string(),
[](string lhs, const string &rhs) { return lhs.empty() ? rhs : lhs + ' ' + rhs; }
);
cout << s << endl;
return 0;
}
出力:
fee fi foe fum
C++ 11では、stringstreamの方法はそれほど怖くありません:
#include <vector>
#include <string>
#include <algorithm>
#include <sstream>
#include <iostream>
int main()
{
std::vector<std::string> v{"Hello, ", " Cruel ", "World!"};
std::stringstream s;
std::for_each(begin(v), end(v), [&s](const std::string &elem) { s << elem; } );
std::cout << s.str();
}