当第二个依赖于第一个时,如何在构造函数初始化列表中初始化两个 std::arrays

How to initialize two std::arrays in constructor initializer list when second depends on first

在构造函数初始化器列表中初始化 std::array class 成员的各种方法中,我发现可变参数包最有效。

但是,如何初始化第二个 std::array 依赖于第一个的成员?

这里的示例是一个 struct polygon,它采用 Vector2 个顶点的可变参数包。我们还从这些顶点构造线。这些行再次存储顶点(考虑到 Vector2 的典型 16 字节大小,引用或指针似乎是不必要的)成对 line.startline.end 并且还预先计算行的长度和法向量以提高性能.

注意:我不想使用两个 std::vectors - 这就是工作代码已经存在的。

对于这种情况,有没有办法将逻辑从构造函数主体移动到构造函数初始化列表?

template <unsigned N, typename Number = double>
struct polygon {

    std::array<Vector2<Number>, N> vertices;
    std::array<Line<Number>, N>    lines; // this is calling Line's default constructor: TOO EARLY

    template <typename... Pack>
    polygon(Pack... vs)
        : vertices{vs...} /* , lines{ .... ??? } this is where we need to construct / initialize lines */ {
        static_assert(sizeof...(vs) == N, "incorrect number of vertices passed");
        for (auto i = 0UL; i != vertices.size(); ++i) {
            // this is calling Line's copy assignment operator: TOO LATE
            lines[i] = Line(vertices[i], vertices[(i + 1) % vertices.size()]);
        }
    }
// ...
}

using Vec2  = Vector2<double>;

using triangle      = polygon<3>;
using quadrilateral = polygon<4>;
using pentagon      = polygon<5>;

int main() {

    auto tri = triangle(Vec2{1, 2}, Vec2{3, 4}, Vec2{4, 5});
    std::cout << tri << "\n";
    // this would be even nicer, but doesn't work: "substitution failure: deduced incomplete pack"
    // auto tri = triangle({1, 2}, {3, 4}, {4, 5});

}

您可以将图案放在 ... 前面,而不仅仅是包装。但是,这需要合适的包装。在这种情况下,一个有用的包将是索引,因此您可以为此制作一个助手:

template<std::size_t... Is>
auto make_lines(std::index_sequence<Is...>) {
    return std::array<Line<Number>, N>{
        Line(vertices[Is], vertices[(Is + 1) % vertices.size()])...
    };
}

std::index_sequence 只是一堆 std::size_t 的标准持有人类型,没有自己的行为。

然后在调用时创建包:

polygon(Pack... vs)
    : vertices{vs...},
      lines(make_lines(std::index_sequence_for<Pack...>{})) { ... }

std::index_sequence_for 获取 Pack 的元素并创建一个序列 0, 1, 2, ..., N-1,每个元素一个。也就是说,它是一个从 0 到 sizeof...(Pack) - 1 的序列,非常适合索引。