将 vector<std::string> 转换为 vector<double>

7
我有一个字符串向量,如{"1.2","3.4","0.5","200.7"}
我想将每个元素转换为double,并存储在vector<double>中。
像这样{1.2,3.4,0.5,200.7} 最好的方法是什么?
我知道std::stod(string, size);但我希望有更好的方法来完成此操作。
我正在寻找类似于:
vector<double> doubleVector = convertStringVectortoDoubleVector(myStringVector);

似乎没有类似的东西,那么下一个最好的选择是什么?


编辑: 这是我最终使用的:

std::vector<double> convertStringVectortoDoubleVector(const std::vector<std::string>& stringVector){
std::vector<double> doubleVector(stringVector.size());
std::transform(stringVector.begin(), stringVector.end(), doubleVector.begin(), [](const std::string& val)
                 {
                     return stod(val);
                 });
return doubleVector;}

要获取完整的答案,请查看Zac Howland和Chris Jester-Young的答案。(注:这完全基于Zac的答案)谢谢。


你不能在不迭代所有元素的情况下转换所有元素。如果你想要懒惰地进行转换,那就是另外一个问题了。 - Bryan Chen
1
据我所知,没有这样的东西:“在不迭代整个向量并将其应用于每个元素的情况下执行此操作”。但是您可以编写一个名为convertVectortoDouble的函数。 - P0W
1
因为避免三行代码而在stackoverflow上浪费半个小时以上的时间是不值得的。 - Tony Delroy
1
如果你编写了自己的名为convertVectortoDouble的函数,那么这是否也会使你的代码看起来更加清晰?我非常支持学习新事物,但通常简单的方法是最好的。 - john
1
@ShayanRC 关于你的编辑,不要按值传递stringVector,而是优先考虑通过const引用进行传递。否则,所有这些关于性能的讨论都将被浪费掉。(但你确实必须按值返回doubleVector,以确保返回类型正确。) - C. K. Young
显示剩余4条评论
4个回答

14

为了完整性(因为Chris从他的答案中移除了编辑部分):

std::vector<double> doubleVector(stringVector.size());
std::transform(stringVector.begin(), stringVector.end(), doubleVector.begin(), [](const std::string& val)
{
    return std::stod(val);
});

使用std::back_inserter而不使用reserve的比较

没有预留空间,每次back_inserter调用push_back时都有可能需要重新调整数组大小。它必须检查当前大小是否超过当前容量,如果需要增加容量,则会将向量复制到新位置(具有增加的容量)。所有这些完成后,它将增加大小并插入新元素。当您知道应该从哪里开始时,这是很多开销(它将与stringVector的大小匹配)。

使用std::back_inserterreserve的比较

预留适当数量的内存将防止重新分配问题,但push_back仍然更新大小并在每次迭代中检查是否已达到容量。你已经大大减少了开销(不再因为大小问题而有“移动”数组的风险),但是你仍然有很多不必要的条件检查。

最初设置大小有一个小开销,即将所有元素设置为默认值(在双重情况下为0.0)。每次迭代,您只需设置当前元素的值。因此,对于N个元素的向量,您有2N + 2个赋值操作(设置容量,大小,元素的初始值和实际元素的值),没有不必要的条件检查。预留方法需要2N + 1个赋值操作(一次设置容量,N次更新大小以及设置N个双精度数值),此外还有N个条件检查。

如果您真的想进一步优化它,可以创建自己的迭代器包装器来进行转换,这将使您能够在初始化向量时编写双精度数的正确值:

// pseudo-code
std::vector<double> doubleVector(my_string_conversion_iterator(stringVector.begin()), my_string_conversion_iterator(stringVector.end());

完整性加一点赞!我的答案通过解释在哪种情况下应该首选每种方法来扩展它。特别是对于目标类型不是默认可构造和/或可分配的通用函数,这种方法是不可行的。 - C. K. Young
关于自定义迭代器方法,除非它模拟了随机访问迭代器(特别是distance应该在常数时间内运行),否则性能不太可能比back_inserter方法更好。 - C. K. Young
嗯,为了被插入到向量中,类型必须是可分配的,但总的来说,我同意。如果包装器被编写为字符串向量自己的迭代器的薄包装器(并且仅修改解引用运算符以返回双精度而不是字符串),则自定义迭代器方法只会更有效率。它允许您合并非back_inserterback_inserter方法的优点。 - Zac Howland
2
顺带一提:Boost的“transform_iterator”非常类似于这种自定义迭代器所做的操作。 - Zac Howland

9

您应该使用std::transform将转换应用于每个元素。

vector<double> doubleVector;
doubleVector.reserve(stringVector.size());
transform(stringVector.begin(), stringVector.end(), back_inserter(doubleVector),
        [](string const& val) {return stod(val);});

正如Zac Howland所指出的那样,这里有另一种方法,它首先使用默认构造的元素初始化向量,然后简单地填充向量的正确值:
vector<double> doubleVector(stringVector.size());
transform(stringVector.begin(), stringVector.end(), doubleVector.begin(),
        [](string const& val) {return stod(val);});

这种方法的优点是向量仅需要调整大小一次,而不是不断增长。缺点是必须首先默认构造向量元素,然后再重新分配正确的值。 对于满足以下所有条件的元素类型,这种折衷是值得的:
  1. 可以默认构造
  2. 默认构造成本低廉
  3. 可以使用相同类型的值进行赋值
  4. 赋值的成本低廉

在此示例中,双精度浮点数完全满足这四个要求,因此后一种方法更好。对于其他类型和特别是编写执行此操作的函数模板时,默认实现应使用前一种方法。

1
如果您将双精度向量的大小设置为字符串向量的大小,则可以避免使用 back_inserter - Zac Howland
1
@ZacHowland 的确如此,避免重新分配内存的方法(而不是避免使用 back_inserter)是使用 vector::reserve。:-P(我非常不喜欢默认构造对象,然后稍后再分配它们,更喜欢在第一次构造时就构造正确的值,所以是的,我拒绝了你的编辑。) - C. K. Young
除非您想要提高性能(这个特定问题并不是真正的问题),否则 back_inserter 每次都会调用 push_back,这意味着它每次都在更新 size 成员(并且每次都在检查 capacity)。使用默认大小构造函数一次设置 size 成员,代价是对双精度向量进行非常便宜的默认初始化。简而言之,您只是为了避免两次设置双精度值而交换了大量的 CPU 时间。 - Zac Howland
@ZacHowland 确实,有时候这个额外的更新/大小检查的成本比额外的初始化和赋值更高。让我在我的答案中更新一些细微差别。 - C. K. Young

2
使用 std::transform
vector<string> str ;
vector<double> dv ;

std::transform(str.begin(), str.end(), back_inserter(dv), [](const string & astr){ return stod( astr) ; } ) ;

你忘记使用 back_inserter 了。另外,看看我的答案。:-P - C. K. Young

1
你可以在C++11中使用std:for_eachlambda
vector<string> a = {"1.2","3.4","0.5","200.7"};
vector<double> b;
for_each(a.begin(), a.end(), [&b](const string &ele) { b.push_back(stod(ele)); });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接