c++ - 了解基于惰性范围的函数的组成
问题描述
TL;博士
下面最后一行注释的行有什么问题?
// headers and definitions are in the down the question
int main() {
std::vector<int> v{10,20,30};
using type_of_temp = std::vector<std::pair<std::vector<int>,int>>;
// seems to work, I think it does work
auto temp = copy_range<type_of_temp>(v | indexed(0)
| transformed(complex_keep_index));
auto w = temp | transformed(distribute);
print(w);
// shows undefined behavior
//auto z = v | indexed(0)
// | transformed(complex_keep_index)
// | transformed(distribute);
//print(z);
}
或者,换句话说,是什么让管道v | indexed(0)
成为transformed(complex_keep_index)
定义明确的,但管道v | indexed(0) | transformed(complex_keep_index)
成为transformed(distribute)
未定义的行为?
扩大的视野
我有一个装东西的容器,
std::vector<int> v{10,20,30};
我有一个函数可以从这些东西中生成另一个容器,
// this is in general a computation of type
// T -> std::vector<U>
constexpr auto complex_comput = [](auto const& x){
return std::vector{x,x+1,x+2}; // in general the number of elements changes
};
所以如果我申请complex_comput
to v
,我会得到,
{{10, 11, 12}, {20, 21, 22}, {30, 31, 32}}
如果我还要连接结果,我最终会得到这个:
{10, 11, 12, 20, 21, 22, 30, 31, 32}
但是,我想跟踪每个数字来自的索引,结果将编码如下:
0 10
0 11
0 12
1 20
1 21
1 22
2 30
2 31
2 32
为了做到这一点,我(最终)想出了这个解决方案,我试图利用 Boost. 具体来说,我执行以下操作:
- 用于
boost::adaptors::indexed
将索引附加到v
- 将每个结果“对”转换为
std::pair
存储应用程序的index
结果和结果,complex_comput
value
- 最后将每个转换
std::pair<st::vector<int>,int>
为std::vector<std::pair<int,int>>
.
但是,我不得不放弃 2 到 3 之间的范围,std::vector
在两个转换之间使用辅助函数“true”。
#include <boost/range/adaptor/indexed.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <iostream>
#include <utility>
#include <vector>
using boost::adaptors::indexed;
using boost::adaptors::transformed;
using boost::copy_range;
constexpr auto complex_comput = [](auto const& x){
// this is in general a computation of type
// T -> std::vector<U>
return std::vector{x,x+1,x+2};
};
constexpr auto complex_keep_index = [](auto const& x){
return std::make_pair(complex_comput(x.value()), x.index());
};
constexpr auto distribute = [](auto const& pair){
return pair.first | transformed([n = pair.second](auto x){
return std::make_pair(x, n);
});
};
template<typename T>
void print(T const& w) {
for (auto const& elem : w) {
for (auto i : elem) {
std::cout << i.second << ':' << i.first << ' ';
}
std::cout << std::endl;
}
}
int main() {
std::vector<int> v{10,20,30};
using type_of_temp = std::vector<std::pair<std::vector<int>,int>>;
auto temp = copy_range<type_of_temp>(v | indexed(0)
| transformed(complex_keep_index));
auto w = temp | transformed(distribute);
print(w);
//auto z = v | indexed(0)
// | transformed(complex_keep_index)
// | transformed(distribute);
//print(z);
}
实际上,对定义和使用的行进行注释可以z
为您提供一个可以编译但生成垃圾结果的代码,即未定义的行为。请注意,应用copy_range<type_of_temp>
到第一个工作范围是必要的,否则生成的代码本质上与 . 右侧的代码相同auto z =
。
为什么我必须这样做?导致oneliner无法工作的细节是什么?
我部分理解了原因,我将在下面列出我的理解/想法,但我提出这个问题是为了彻底解释所有细节。
- 我知道我观察到的未定义行为源于
z
一个范围,该范围定义了一些已被破坏的临时视图; - 鉴于代码的工作版本,很明显临时是
v | indexed(0) | transformed(complex_keep_index)
; - 然而,它
v | indexed(0)
本身不是一个被喂食的临时物transformed(complex_keep_index)
吗? - 一个重要的细节可能是该表达式
v | indexed(0)
只不过是一个惰性范围,它不计算任何内容,但只是设置了一些东西,以便在迭代范围时完成计算;毕竟我可以很容易地做到v | indexed(0) | indexed(0) | indexed(0)
,这是明确的; - 而且整个
v | indexed(0) | transformed(complex_keep_index)
定义都很好,否则上面使用的代码w
可能会行为不端(我知道 UB 并不意味着结果必须显示有问题,此时在这个硬件上看起来一切正常,并且明天休息)。 transformed(distribute)
因此,将右值传递给;本质上是错误的。- 但是这样做的错误在于
distribute
,而不是transformed
,因为例如更改distribute
为[](auto x){ return x; }
似乎已明确定义。 - 那么有什么问题
distribute
呢?这是代码
constexpr auto distribute = [](auto const& pair){
return pair.first | transformed([n = pair.second](auto x){
return std::make_pair(x, n);
});
};
- 它有什么问题?返回的范围( this 的输出
transformed
)将包含一些迭代器/指针/引用,这些迭代器/指针/引用在返回pair.first
时超出了范围distribute
,但pair
它是对调用者中某些东西的引用,它一直存在,对吧? - 但是我知道,即使
const
引用(例如pair
)可以使临时(例如 的元素v | indexed(0) | transformed(complex_keep_index)
)保持活动状态,但这并不意味着当该引用超出范围时临时保持活动状态只是因为它又被其他东西引用(输出中的引用/指针/迭代器transformed([n = …](…){ … })
)不会超出范围。
我认为/希望答案可能已经在我上面写的内容中,但是我需要一些帮助来简化所有这些,以便我可以一劳永逸地理解它。
解决方案
推荐阅读
- c# - 无法通过 FormData Append 传递 HTML 内容
- c++ - 如何将这些串行端口的参数传递给函数
- node.js - 如何修复“TypeError:Buffer.from 不是函数”?
- python - 静态文件在 django nginx 中不起作用
- angular - 带选择的垂直角材料表
- c# - 使用 Microsoft.Azure.Cosmos 的 FeedResponse 时如何获取单个文档的 ETag
- javascript - 如何使用现有数组值中的动态键映射新数组而不循环?
- html - 如何禁用html css上的阴影
- javascript - 从 JavaScript 变量访问 Django 变量 [已解决]
- python - 如何从包含许多字典的列表中获得每个玩家的高分?每个字典都是一个名字和一个分数