You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何连接向量视图后无法得到合法的const input_range?

问题:笛卡尔积transform后join生成的range无法作为const input_range使用

尝试对笛卡尔积执行transform操作生成网格边集,再用std::views::join展平结果,但该range无法作为const input_range传递给函数,取消注释代码中的static_assert后编译报错。

代码示例

#include <ranges>
#include <vector>
#include <iostream>
#include <tuple>
#include <iterator>

int main() {
    auto indicies = std::views::iota(0, 3);

    auto coords = std::views::cartesian_product(indicies, indicies);

    auto edges = coords | std::views::transform([](const auto& coord) {
        using Coord = std::pair<std::size_t, std::size_t>;
        using Edge = std::pair<Coord, Coord>;
        std::vector<Edge> ret;
        const auto& [x, y] = coord;
        auto curr = Coord{x, y};
        if (x + 1 < 3) {
            auto dest = Coord{x + 1, y};
            ret.emplace_back(curr, dest);
        }
        if (y + 1 < 3) {
            auto dest = Coord{x, y + 1};
            ret.emplace_back(curr, dest);
        }
        return ret;
    }) | std::views::join;

    std::cout << edges.cbegin()->first.first << std::endl;

    // static_assert(std::ranges::input_range<const decltype(edges)>);//  <- 取消注释后编译失败
    return 0;
}

编译错误信息(g++-13)

<source>: In function 'int main()':
<source>:31:32: error: static assertion failed
   31 |     static_assert(std::ranges::input_range<const decltype(edges)>);//  <- does not compile with message that ;
      |                   ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:31:32: note: constraints not satisfied
In file included from /opt/compiler-explorer/arm/gcc-13.2.0/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/include/c++/13.2.0/string_view:48,
                 from /opt/compiler-explorer/arm/gcc-13.2.0/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/include/c++/13.2.0/bits/basic_string.h:47,
                 from /opt/compiler-explorer/arm/gcc-13.2.0/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/include/c++/13.2.0/string:54,
                 from /opt/compiler-explorer/arm/gcc-13.2.0/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/include/c++/13.2.0/bits/locale_classes.h:40,
                 from /opt/compiler-explorer/arm/gcc-13.2.0/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/include/c++/13.2.0/bits/ios_base.h:41,
                 from /opt/compiler-explorer/arm/gcc-13.2.0/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/include/c++/13.2.0/streambuf:43,
                 from /opt/compiler-explorer/arm/gcc-13.2.0/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/include/c++/13.2.0/bits/streambuf_iterator.h:35,
                 from /opt/compiler-explorer/arm/gcc-13.2.0/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/include/c++/13.2.0/iterator:66,
                 from /opt/compiler-explorer/arm/gcc-13.2.0/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/include/c++/13.2.0/ranges:43,
                 from <source>:1:
/opt/compiler-explorer/arm/gcc-13.2.0/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/include/c++/13.2.0/bits/ranges_base.h:501:13:   required for the satisfaction of 'range<_Tp>' [with _Tp = const std::ranges::join_view<std::ranges::transform_view<std::ranges::cartesian_product_view<std::ranges::iota_view<int, int>, std::ranges::iota_view<int, int> >, main::_anon_155> >]
/opt/compiler-explorer/arm/gcc-13.2.0/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/include/c++/13.2.0/bits/ranges_base.h:501:21:   in requirements with '_Tp& __t' [with _Tp = const std::ranges::join_view<std::ranges::transform_view<std::ranges::cartesian_product_view<std::ranges::iota_view<int, int>, std::ranges::iota_view<int, int> >, main::_anon_155> >]
/opt/compiler-explorer/arm/gcc-13.2.0/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/include/c++/13.2.0/bits/ranges_base.h:503:22: note: the required expression 'std::ranges::__cust::begin(__t)' is invalid
  503 |         ranges::begin(__t);
      |         ~~~~~~~~~~~~~^~~~~
/opt/compiler-explorer/arm/gcc-13.2.0/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/include/c++/13.2.0/bits/ranges_base.h:504:20: note: the required expression 'std::ranges::__cust::end(__t)' is invalid
  504 |         ranges::end(__t);
      |         ~~~~~~~~~~~^~~~~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
Compiler returned: 1

问题原因

你的transform lambda返回的是临时std::vector<Edge>对象,std::views::transform生成的transform_view在迭代时,每次解引用都会创建这个临时vector。而std::views::join要将这些临时容器展平为一个range时,其const版本无法满足input_range的概念要求:

  • 当join_view被const限定后,底层的transform_view也会变成const,其迭代器解引用得到的是临时vector的const引用,但join_view需要遍历这个临时容器的元素,而临时对象的生命周期管理在const range的语境下无法符合标准库的range概念约束,导致无法调用begin()和end()。

解决方法

方法1:返回view而非临时容器

将transform返回的vector替换为标准库view(如std::views::single+std::views::concat),这样transform返回的是一个轻量级view而非临时容器,view的const版本天然符合range要求,对应的const join_view也能满足input_range:

#include <ranges>
#include <vector>
#include <iostream>
#include <tuple>
#include <iterator>

int main() {
    auto indicies = std::views::iota(0, 3);
    auto coords = std::views::cartesian_product(indicies, indicies);

    auto edges = coords | std::views::transform([](const auto& coord) {
        using Coord = std::pair<std::size_t, std::size_t>;
        using Edge = std::pair<Coord, Coord>;
        const auto& [x, y] = coord;
        auto curr = Coord{x, y};

        // 用view替代vector存储边
        auto right_edges = (x + 1 < 3) 
            ? std::views::single(Edge{curr, Coord{x+1, y}}) 
            : std::views::empty<Edge>();
        auto down_edges = (y + 1 < 3)
            ? std::views::single(Edge{curr, Coord{x, y+1}})
            : std::views::empty<Edge>();
        
        return std::views::concat(right_edges, down_edges);
    }) | std::views::join;

    std::cout << edges.cbegin()->first.first << std::endl;
    static_assert(std::ranges::input_range<const decltype(edges)>); // 现在可以通过编译
    return 0;
}

方法2:提前materialize为容器

如果必须使用vector存储中间结果,可以先将transform的结果转换为一个实际的容器(如std::vector<std::vector<Edge>>),再对这个容器执行join操作。此时join_view的底层是一个const容器,其元素是const vector,而const vector本身是合法的range,因此const join_view可以满足input_range:

#include <ranges>
#include <vector>
#include <iostream>
#include <tuple>
#include <iterator>

int main() {
    auto indicies = std::views::iota(0, 3);
    auto coords = std::views::cartesian_product(indicies, indicies);

    // 先将transform的结果转换为实际容器
    auto temp_edges = coords | std::views::transform([](const auto& coord) {
        using Coord = std::pair<std::size_t, std::size_t>;
        using Edge = std::pair<Coord, Coord>;
        std::vector<Edge> ret;
        const auto& [x, y] = coord;
        auto curr = Coord{x, y};
        if (x + 1 < 3) {
            ret.emplace_back(curr, Coord{x+1, y});
        }
        if (y + 1 < 3) {
            ret.emplace_back(curr, Coord{x, y+1});
        }
        return ret;
    }) | std::ranges::to<std::vector>();

    auto edges = temp_edges | std::views::join;

    std::cout << edges.cbegin()->first.first << std::endl;
    static_assert(std::ranges::input_range<const decltype(edges)>); // 现在可以通过编译
    return 0;
}

内容的提问来源于stack exchange,提问作者Георгий Гуминов

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 03:45:55