当第二个依赖于第一个时,如何在构造函数初始化列表中初始化两个 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.start
和 line.end
并且还预先计算行的长度和法向量以提高性能.
注意:我不想使用两个 std::vector
s - 这就是工作代码已经存在的。
对于这种情况,有没有办法将逻辑从构造函数主体移动到构造函数初始化列表?
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
的序列,非常适合索引。
在构造函数初始化器列表中初始化 std::array
class 成员的各种方法中,我发现可变参数包最有效。
但是,如何初始化第二个 std::array
依赖于第一个的成员?
这里的示例是一个 struct polygon
,它采用 Vector2
个顶点的可变参数包。我们还从这些顶点构造线。这些行再次存储顶点(考虑到 Vector2 的典型 16 字节大小,引用或指针似乎是不必要的)成对 line.start
和 line.end
并且还预先计算行的长度和法向量以提高性能.
注意:我不想使用两个 std::vector
s - 这就是工作代码已经存在的。
对于这种情况,有没有办法将逻辑从构造函数主体移动到构造函数初始化列表?
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
的序列,非常适合索引。