通过可变模板进行通用聚合初始化
Universal aggregate initialization by variadic templates
本文关键字:初始化 更新时间:2023-10-16
这是我刚才想到的一个有趣的问题。给定具有底层聚合的struct
:
#include <array>
template <typename T, size_t N>
struct A
{
constexpr A() = default;
template <typename ... Ts>
constexpr A(const T& value, const Ts& ... values); // magic
std::array<T, N> arr; // aggregate
};
如何将可变模板构造函数A(const T& value, const Ts& ... values)
实现到
- 接受类型为
T
的值和另一个A<T, N>
的值 - 根据传递的参数表示的值正确初始化基础聚合
- 尊重骨料的能力
- 支持C++14 constexpr规则,并且不引入任何运行时开销
满足上述要求,可以执行以下操作:
int main()
{
A<int, 3> x(1, 2, 3);
A<int, 2> y(1, 2);
A<int, 6> a(x, 1, 2, 3);
A<int, 6> b(1, x, 2, 3);
A<int, 6> c(1, 2, x, 3);
A<int, 6> d(1, 2, 3, x);
A<int, 6> e(x, x);
A<int, 6> f(y, y, y);
return 0;
}
以下是一种有效的方法,但几乎可以肯定的是,它可以在.上得到改进
我们有一个A
的构造函数,它接受一个参数包,将每个元素转换为一个元组,将元组连接在一起形成一个大元组,然后简单地使用该大元组的聚合初始化。以下所有内容都可以是constexpr
,为了简洁起见,我省略了它。
首先我们进行转换:
template <class... Us>
A(Us const&... us)
: A(std::tuple_cat(as_tuple(us)...))
{ }
带有:
// single argument
template <class U>
auto as_tuple(U const& u) {
return std::forward_as_tuple(u);
}
// aggregate argument
template <size_t M>
auto as_tuple(A<T, M> const& a) {
return as_tuple(a, std::make_index_sequence<M>{});
}
template <size_t M, size_t... Is>
auto as_tuple(A<T, M> const& a, std::index_sequence<Is...> ) {
return std::forward_as_tuple(std::get<Is>(a.arr)...);
}
然后我们从那里初始化:
template <class... Us, class = std::enable_if_t<(sizeof...(Us) <= N)>>
A(std::tuple<Us...> const& t)
: A(t, std::index_sequence_for<Us...>{})
{ }
template <class... Us, size_t... Is>
A(std::tuple<Us...> const& t, std::index_sequence<Is...> )
: arr{{std::get<Is>(t)...}}
{ }
Demo
@Barry的回答当然是正确的,也是可以接受的。但它需要添加一些C++14库(您可能也可以用C++11自己编写),总体上需要一些好的tuple
和元编程功能。
让我们将多个参数视为"范围范围",其中范围只是一个指针和一个大小。标量自变量只是大小为1的范围,而A<T, N>
自变量是大小为N的范围。
template<class T>
struct Range
{
T const* data_;
std::size_t size_;
constexpr T const* begin() const noexcept { return data_; }
constexpr T const* end() const noexcept { return data_ + size_; }
constexpr std::size_t size() const noexcept { return size_; }
};
template<class T>
constexpr Range<T> as_range(T const& t)
{
return { &t, 1 };
}
template<class T, std::size_t N>
struct A;
template<class T, std::size_t N>
constexpr Range<T> as_range(A<T, N> const& a)
{
return { a.arr, N };
}
然后你可以简单地在所有范围的的所有元素上进行双循环
template <typename T, size_t N>
struct A
{
T arr[N]; // aggregate
constexpr A() = default;
template <typename U, typename... Us>
constexpr A(U const u, Us const&... us)
:
arr{}
{
Range<T> rngs[1 + sizeof...(Us)] { as_range(u), as_range(us)... };
auto i = 0;
for (auto const& r : rngs)
for (auto const& elem : r)
arr[i++] = elem;
assert(i == N);
}
};
实时示例在编译时工作(要求GCC>=6.0或Clang>=3.4)
template <class T, size_t N>
void print(A<T, N> const& a) {
for (T const& t : a.arr) {
std::cout << t << ' ';
}
std::cout << 'n';
}
int main()
{
constexpr A<int, 3> x(1, 2, 3);
constexpr A<int, 2> y(1, 2);
constexpr A<int, 6> a(x, 1, 2, 3);
constexpr A<int, 6> b(1, x, 2, 3);
constexpr A<int, 6> c(1, 2, x, 3);
constexpr A<int, 6> d(1, 2, 3, x);
constexpr A<int, 6> e(x, x);
constexpr A<int, 6> f(y, y, y);
print(a); // 1 2 3 1 2 3
print(b); // 1 1 2 3 2 3
print(c); // 1 2 1 2 3 3
print(d); // 1 2 3 1 2 3
print(e); // 1 2 3 1 2 3
print(f); // 1 2 1 2 1 2
}
相关文章:
- 是否可以初始化不可复制类型的成员变量(或基类)
- C++使用整数的压缩数组初始化对象
- C++初始化基类
- 多成员Constexpr结构初始化
- 复制列表初始化的隐式转换的等级是多少
- 内联映射初始化的动态atexit析构函数崩溃
- 如何在C++中初始化嵌套类中的2个memeber
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- 没有用于初始化C++中的变量模板的匹配构造函数
- 在未初始化映射的情况下,将值插入到映射的映射中
- C++成员初始化
- 为什么在C++中首先初始化成员类
- 同时具有"聚合初始化"和"模板推导"
- 初始化具有非默认构造函数的std::数组项的更好方法
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 在C和C++中初始化结构中的数组
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 在函数内部的声明中初始化数组,并在外部使用它
- 继承:构造函数,初始化C++11中基类的类C数组成员