c ++标签下投票最多的问题之一称为"Splitting a string in C++"。在其中,提问者问道:“用C ++分割字符串最优雅的方法是什么?”。
The highest voted answer这个问题提供了这两个功能:
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
return split(s, delim, elems);
}
这些功能很棒。但我试图理解为什么回答者没有将这两个函数组合成一个函数。是否有一些性能,可用性或可读性的好处,我没有看到你在结合这些功能时错过了什么?具有组合功能的完整程序如下:
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
// splitting them into two seperate functions is unnecessary it seems to me, and makes the underlying function harder to understand.
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
int main()
{
std::vector<std::string> x = split("one:two::three", ':');
for (int i = 0; i<x.size(); ++i) cout << x[i] << '\n';
return 0;
}
我发现这个功能分裂得非常不那么优雅 - 而且更难理解 - 但我觉得我必须遗漏一些东西。他为什么不把它们结合起来?
答案 0 :(得分:4)
想象一下,您将分割一堆不同的来源,但希望结果都以一个容器结束。为此,您不希望该函数始终为您分配新容器:
之前,需要额外的工作:
std::vector<std::string> sources = /* populate */;
std::vector<std::string> results;
for (const auto& source : sources)
{
auto result = split(source, ":");
// or maintain a vector of vectors...yuck
results.insert(std::make_move_iterator(result.begin()),
std::make_move_iterator(result.end()));
}
之后,直截了当:
std::vector<std::string> sources = /* populate */;
std::vector<std::string> results;
for (const auto& source : sources)
{
split(source, ":", results);
}
或者:想象一下你要分割一堆差异来源,为了提高效率,你想要最小化内存分配(你的分析器表示你在这里分配的太多了,例如)。因此,您反复使用相同的向量,以避免在第一次拆分后的后续内存分配。
之前,慢:
std::vector<std::string> sources = /* populate */;
for (const auto& source : sources)
{
auto result = split(source, ":");
process(result);
}
之后,更好:
std::vector<std::string> sources = /* populate */;
std::vector<std::string> result;
for (const auto& source : sources)
{
result.clear();
split(source, ":", result);
process(result);
}
当然,在一般情况下,为您创建容器的简单性很好,我们可以轻松地重复使用更通用的功能,以较低的成本创建第二个功能。
答案 1 :(得分:3)
您从原始代码复制的代码提供了两个语义略有不同的函数。第一个将添加到现有向量中,即分割结果,而第二个构建在第一个向量之上,创建一个新向量,仅包含来自此 split
通过提供两种功能,您可以提供(具有相同的成本)两种不同的行为,可以满足不同用户的不同需求。如果将它们合并到单个版本中,并且用户需要构建包含从拆分多个字符串获得的所有标记的列表,则必须创建多个向量并合并它们。
有趣的一点,实际上是实际的实现,而不是关于设计一个/两个功能的问题的一部分。第二个功能应该实现为:
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
split(s, delim, elems);
return elems;
}
不同之处在于,您应该直接返回本地变量,而不是返回从内部调用split
获得的引用。此更改启用了局部变量的命名返回值优化,从而消除了副本 * 的成本。
* 在C ++ 11中,你也可以使用return std::move(split(s,delim,elems));
来获得相同的行为,但这需要更多的击键,而且仍然是 move 而不是删除整个操作。