首页 > 解决方案 > 了解基于惰性范围的函数的组成

问题描述

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_computto 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. 具体来说,我执行以下操作:

  1. 用于boost::adaptors::indexed将索引附加到v
  2. 将每个结果“对”转换为std::pair存储应用程序的index结果和结果,complex_computvalue
  3. 最后将每个转换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无法工作的细节是什么?

我部分理解了原因,我将在下面列出我的理解/想法,但我提出这个问题是为了彻底解释所有细节。

constexpr auto distribute = [](auto const& pair){
    return pair.first | transformed([n = pair.second](auto x){
        return std::make_pair(x, n);
    });
};

我认为/希望答案可能已经在我上面写的内容中,但是我需要一些帮助来简化所有这些,以便我可以一劳永逸地理解它。

标签: c++boostlazy-evaluationundefined-behaviorboost-adaptors

解决方案


推荐阅读