如何逐步连接范围?

How to incrementially concatenate ranges?

背景

我想做的是实现一些表示几何的 classes。几何体的任何实例 class 都有一个名为 vertices() 的方法,该方法 returns 是一个非拥有的顶点视图。几何 class 可以用多个其他几何 classes 来表示,因此几何 class' vertices() 方法理想情况下只会做这样的事情(伪代码):

vertices()
{
  return join(part1.vertices(), part2.vertices(), part3.vertices());
}

以不复制或移动顶点为前提。

在 C++20 中,我相信可以用范围和视图来完成,但我不知道该怎么做。

我的尝试

#include <iostream>
#include <ranges>
#include <vector>

struct Vertex { float x, y, z; };

struct GeometryA {
    auto vertices() {
        return std::ranges::ref_view(v);
    }
    std::vector<Vertex> v {{0.0f, 0.0f, 1.0f}};
};

struct GeometryB {
    auto vertices() {
        return std::ranges::ref_view(v);
    }
    std::vector<Vertex> v {{0.0f, 1.0f, 0.0f}};
};

struct GeometryC {
    auto vertices() {
        // OK: All elements of vector are of same type
        return std::vector{ a.vertices(), b.vertices(), std::ranges::ref_view(v)} | std::views::join;
    }
    GeometryA a;
    GeometryB b;
    std::vector<Vertex> v {{0.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}};
};

struct GeometryD {
    auto vertices() {
        // Compilation fails: Elements of vector have different types
        return std::vector{ c.vertices(), std::ranges::ref_view(v)} | std::views::join;
    }
    GeometryC c;
    std::vector<Vertex> v {{1.0f, 0.0f, 1.0f}};
};

int main() {
    GeometryD d;

    for(Vertex const& vertex : d.vertices()) {
        // Should print {0,0,1} {0,1,0} {0,1,1} {1,0,0} {1,0,1}
        std::cout << "{" << vertex.x << "," << vertex.y << "," << vertex.z << "} ";
    }
    
    return 0;
}

编译在 GeometryD::vertices 中失败,因为我试图从初始化时的元素(c.vertices()std::ranges::ref_view(v))中推导出最外层向量的模板参数 T,但是它们没有相同的类型,因此无法推导出 T

我不知道如何解决这个问题。

问题

是否可以使用标准范围库来递增地连接范围?


我想我可以通过使用一些递归模板技巧来收集几何体 class 直接或间接拥有的所有顶点数据,然后只使用 std::views::join 一次,但在我动手之前,我想就我当前的尝试获得一些意见。

您可以使用 Eric Niebler 的 Range-v3 库来完成此操作。

只需用 ranges::views::concat 连接不同的范围视图。例如,对于 GeometryC:

return ranges::views::concat(a.vertices(), b.vertices(), v);

[Demo]

Range-v3 的大部分内容正逐渐被标准 Ranges 库采用,尽管似乎此功能尚未实现。