将std::vector<boost::optional<double>>转换为std::vector<double>

9

我有一个名为foostd::vector<boost::optional<double>>。在这种特定情况下,我需要一个std::vector<double>,其中来自另一个向量的任何“可选”元素都映射到新向量中的0。

我是否遗漏了一个一行代码的解决方案?

另一种选择是令人不满意的。

std::vector<double> out(foo.size());
for (auto& it : foo){
    out.push_back(it ? *it : 0.0);
}

我希望能够使用基于std::optional的解决方案,尽管我尚未使用该标准。


2
可以使用 std::transform。以及 boost::optional::value_or - Jarod42
6
不满意 - 我真的不明白为什么。 - StoryTeller - Unslander Monica
4
@BoBTFish的建议是使用range-v3库,解决方案可以是:auto out = foo | ranges::view::transform([](const auto& o) { return o.value_or(0.0); });。这行代码将foo中的每个元素转换为value_or(0.0)的值,并赋给out变量。 - Jarod42
4
“it”是一个糟糕的名称,因为它不是迭代器,而是一个“可选项(optional)”。 - Jarod42
1
你确定你的示例代码是好的吗?你首先在构造函数中添加了foo.size()个值为0.0的元素,然后在循环中又添加了foo.size()个元素。(带有int参数的构造函数并没有保留那个空间;它实际上是添加了这些元素) - Mauricio
显示剩余6条评论
2个回答

15

std::transform 解决方案:

std::vector<double> out(foo.size());
std::transform(foo.begin(), foo.end(), out.begin(), [](const auto& opt){ return opt.value_or(0.0); });

编辑:添加了out的定义。


这只是将基于范围的 for 循环强制转换为单个长行,没有任何整洁的效果。而且你没有定义 out,所以你仍然需要额外的一行来定义它。 - BoBTFish
@BoBTFish 的确如此。但我想这已经是没有使用 ranges-v3 的最接近一行的写法了。 - Max Langhof
4
它有一个优点,比裸循环更强大。一旦原帖的作者转换到C++17,他们可以通过对那一行代码进行微小的更改来并行执行这个变换。 - StoryTeller - Unslander Monica
@MaxLanghof,如果使用ranges-v3,它会是什么样子呢? - Arthur Tacca
@ArthurTacca 请查看问题本身的Jarod42的评论。请注意,尽管我相信有一种简单的方法可以添加另一个向量,但它并没有直接给出另一个向量。 - Max Langhof
@MaxLanghof 谢谢,我错过了那个。 - Arthur Tacca

4
这里有一个解决方案,它构建了输出向量并且具有所需的值。但仍无法强制输出在一行上。
auto valueOrZero = [](auto opt){ return opt?*opt:0.0; };
std::vector<double> out(boost::make_transform_iterator(std::begin(foo), valueOrZero), boost::make_transform_iterator(std::end(foo), valueOrZero));

很不幸,boost::transform_iterator 要求对于结束迭代器指定一元转换函数,并且你不能简单地重复 lambda 定义,因为它还要求两个迭代器具有完全相同的类型。 这强制 lambda 函数必须在自己的行上。

我认为可能可以编写一个绕过此问题的转换迭代器,但你必须从头开始操作。


1
不确定,但是 bind(&optional<double>::value_or, _1, 0.0)(替换lambda)可能只需要一行代码。 - Jarod42

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