Ser*_*eyA 6 c++ template-meta-programming variadic-templates constexpr c++17
(扰流器 -这是一个自我回答的问题)让我们假设我有两个索引序列,例如using i1 = std::index_sequence<1, 3, 5, 7>;和using i2 = std::index_sequence<2, 4, 6, 8>;
我想创建一个数组(在编译时),该数组中将依次包含8个元素:1, 2, 3, 4, 5, 6, 7, 8,以便以下代码可以工作(例如,在全局范围内):
std::array<int, 8> arr = make_array(i1{}, i2{});
Run Code Online (Sandbox Code Playgroud)
注意:如果我只想要一个序列,则解决方案很简单:
template<size_t... Ix>
constexpr auto make_arr(std::index_sequence<Ix...> )
return std::array{Ix...};
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我需要连接两个序列,那并不是那么简单,例如,这是行不通的:
template<size_t... Ix1, size_t... Ix2>
constexpr auto make_arr(std::index_sequence<Ix1...>, std::index_sequence<Ix2...>)
return std::array{(Ix1, Ix2)...};
}
Run Code Online (Sandbox Code Playgroud)
(上面的代码将使用第二个序列中的值填充数组)。
另一个可能的解决方案是使用constexpr函数,该函数首先定义一个具有默认值的数组,然后将索引序列中的值复制到该数组中,但是虽然该方法适用于ints,但不适用于某些更为复杂的类型,这些类型不是默认的-可构造的(显然,它们不是索引序列的一部分,但可以是其他东西)。
是否有不需要循环和默认构造值的解决方案?任何可用的C ++标准都是公平的游戏。
到目前为止我知道两种解决方案。
在第一个中,我设法使用 C++ 折叠表达式和运算符重载来完成此操作。这段代码很糟糕,我并不为此感到自豪 - 但它就在那里。欢迎大家发表评论和贡献:
#include <utility>
#include <array>
struct Int {
size_t i;
};
constexpr std::array<Int, 2> operator+(Int i1, Int i2) {
return {i1, i2};
}
template<size_t SZ, size_t... Ix>
constexpr auto concat(std::array<Int, 2> arr1, std::array<Int, SZ> arr2, std::index_sequence<Ix...>)
{
return std::array<Int, SZ+2>{arr1[0], arr1[1], arr2[Ix]...};
}
template<size_t SZ>
constexpr auto operator+ (std::array<Int, 2> arr1, std::array<Int, SZ> arr2) {
return concat(arr1, arr2, std::make_index_sequence<SZ>{});
}
template<size_t SZ, size_t... Ix>
constexpr auto convert_impl(std::array<Int, SZ> arr, std::index_sequence<Ix...>) {
return std::array{arr[Ix].i...};
}
template<size_t SZ>
constexpr auto convert(std::array<Int, SZ> arr) {
return convert_impl(arr, std::make_index_sequence<SZ>{});
}
template<size_t... IX1, size_t... IX2>
constexpr auto make_arr(std::index_sequence<IX1...>, std::index_sequence<IX2...>) {
return convert(((Int{IX1} + Int{IX2})+ ...));
}
using i1 = std::index_sequence<1, 3, 5, 7>;
using i2 = std::index_sequence<2, 4, 6, 8>;
auto k = make_arr(i1{}, i2{});
Run Code Online (Sandbox Code Playgroud)
第二种解决方案是更好的方法:
#include <utility>
#include <array>
template<size_t... Ix, class T, size_t SZ>
auto concat(T t1, T t2, std::array<T, SZ> arr, std::index_sequence<Ix...>) {
return std::array{t1, t2, arr[Ix]...};
}
template<size_t i0, size_t... Ix0, size_t i1, size_t... Ix1>
auto make_array(std::index_sequence<i0, Ix0...>, std::index_sequence<i1, Ix1...>) {
if constexpr (sizeof...(Ix0) > 0) {
return concat(i0,
i1,
make_array(std::index_sequence<Ix0...>{}, std::index_sequence<Ix1...>{}),
std::make_index_sequence<sizeof...(Ix0) + sizeof...(Ix1)>{}
);
} else {
return std::array{i0, i1};
}
}
using i1 = std::index_sequence<1, 3, 5, 7>;
using i2 = std::index_sequence<2, 4, 6, 8>;
std::array<size_t, 8> k = make_array(i1{}, i2{});
Run Code Online (Sandbox Code Playgroud)