微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

理解基于惰性范围的函数的组合

如何解决理解基于惰性范围的函数的组合

TL;DR

下面最后注释的行有什么问题?

// 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,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 应用到 v,我会得到,

{{10,11,12},{20,21,22},{30,31,32}}

如果我也连接结果,我最终会得到这个:

{10,12,22,30,32}

但是,我想跟踪每个数字来自的索引,结果将编码如下:

0 10
0 11
0 12
1 20
1 21
1 22
2 30
2 31
2 32

为了实现这一点,我(最终)想出了这个解决方案,我试图利用 Boost.range 的范围。具体来说,我执行以下操作:

  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 之间的范围,在两个转换之间使用辅助“true”std::vector

#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+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,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);
    });
};
  • 它有什么问题?返回的范围(此 transformed输出)将保存一些指向 pair.first 的迭代器/指针/引用,这是 distribute 返回时超出范围的一部分,但 pair 是对调用者中某些内容的引用,它继续存在,对吗?
  • 但是我知道即使 const 引用(例如 pair)可以保持临时(例如 v | indexed(0) | transformed(complex_keep_index) 的元素)活着,但这并不意味着临时保持当该引用超出范围时仍然活着,只是因为它又被其他没有超出范围的东西(transformed([n = …](…){ … })输出中的引用/指针/迭代器)引用。

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?