因为我喜欢单线(它们对于各种奇怪的东西都非常有用,你会在最后看到),这里有一个使用 std::accumulate 和 C++11 lambda 的解决方案:
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 (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 在评论中指出的那样,当数组以空字符串开头时,上面的代码会出现异常行为,因为“第一次运行”的检查缺少以前没有额外字符的运行,而且 -在所有运行中检查“首次运行”是很奇怪的(即代码未优化)。
如果我们知道列表至少有一个元素,那么这两个问题的解决方案就很容易了。OTOH,如果我们知道列表至少没有一个元素,那么我们可以进一步缩短运行时间。
我认为生成的代码不是那么漂亮,所以我在这里将其添加为The Correct Solution,但我认为上面的讨论仍然有优点:
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]
对于向量也是如此。
- 根据评论和聊天中的讨论,lambda 仍然会进行一些复制。这可以通过使用这个(不太漂亮的)lambda 来最小化:
[](auto&& a, auto&& b) -> auto& { a += ','; a += b; return a; })
它(在 GCC 10 上)将性能提高了 10 倍以上。感谢@Deduplicator 的建议。我仍在试图弄清楚这里发生了什么。