创建具有扩展索引序列的结构
Creating a structure with an expanded index sequence
我希望能够在构造函数的初始化列表中使用参数包扩展。实现此目标的最佳方法是赋予我的 class 参数包模板参数吗?这是我的意思的一个例子:
https://coliru.stacked-crooked.com/a/e699c4cd035e0b1c
#include <utility>
#include <iostream>
template<typename T, std::size_t...Is>
struct base_vec
{
constexpr static std::size_t N = sizeof...(Is);
T e[N];
base_vec() : e{} {}
explicit base_vec(const T& s) : e{((void)Is,s)...} {}
};
template<typename T, std::size_t...Is>
std::ostream& operator<<(std::ostream& lhs, const base_vec<T,Is...>& rhs)
{
return (std::cout << ... << rhs.e[Is]);
}
template<typename T, std::size_t...Is>
constexpr auto getVecIs(std::index_sequence<Is...> seq)
{
return base_vec<T, Is...>{};
}
template<typename T, std::size_t N>
using vec = decltype(getVecIs<T>(std::declval<std::make_index_sequence<N>>()));
int main()
{
vec<int,3> v(2);
std::cout << v << "\n";
return 0;
}
注意微不足道的扩展e{((void)Is,s)...}
。这是一个好的做法,还是我错过了这种方法的一些缺点(除了现在我的结构将有一个完整的参数包,而不是单个 size_t N
)?
您可以将扩展完全移入 class:
template<typename T, std::size_t N>
struct vec
{
public:
T e[N];
vec() : e{} {}
explicit vec(const T& s) : vec{s, std::make_index_sequence<N>{}} {}
private:
template <std::size_t ... Is>
vec(const T& s, std::index_sequence<Is...>) : e{(Is, s)...} {}
};
在我看来,您正在重新创建一个 class 灵感来自 std::array
的一个方便的索引序列来操作。
有趣。
我建议直接定义 vec
的简化(恕我直言),避免 base_vec
,使用模板专业化
#include <utility>
#include <iostream>
template <typename T, std::size_t N, typename = std::make_index_sequence<N>>
struct vec;
template <typename T, std::size_t N, std::size_t...Is>
struct vec<T, N, std::index_sequence<Is...>>
{
T e[N] {} ;
vec (const T& s) : e{((void)Is,s)...}
{ }
};
template <typename T, std::size_t...Is>
std::ostream& operator<< (
std::ostream& lhs,
vec<T, sizeof...(Is), std::index_sequence<Is...>> const & rhs)
{ return (std::cout << ... << rhs.e[Is]); }
int main ()
{
vec<int, 3u> v(2);
std::cout << v << "\n";
}
您可以在 vec
专业的 body 中添加一个 static_assert()
如下
static_assert( N == sizeof...(Is) );
避免"hijacked"使用如下
vec<int, 5u, std::make_index_sequence<12u>> bad_vec_1;
或者,也许更好,
static_assert( std::is_same_v<std::index_sequence<Is...>,
std::make_index_sequence<N>> );
避免aslo
vec<int, 5u, std::index_sequence<1u, 3u, 5u, 2u, 4u>> bad_vec_2;
-- 编辑--
OP 询问
Any ideas how to extend this to multiple index sequences - for multi-dimensional arrays for example?
很简单。
根据 Jarod42 的建议,您可以添加另一个 N
和另一个 std::index_sequence
。
对于 2 dim 的情况,我建议如下。
我还为 N1*N2
添加了第三个索引序列(我想可能会有用)。
我把专业化的body留作练习。
template <typename T, std::size_t N1, std::size_t N2,
typename = std::make_index_sequence<N1>,
typename = std::make_index_sequence<N2>,
typename = std::make_index_sequence<N1*N2>>
struct vec2dim;
template <typename T, std::size_t N1, std::size_t N2,
std::size_t ... Is, std::size_t ... Js, std::size_t ... Ks>
struct vec2dim<T, N1, N2, std::index_sequence<Is...>,
std::index_sequence<Js...>, std::index_sequence<Ks...>>
{
static_assert( std::is_same_v<std::index_sequence<Is...>,
std::make_index_sequence<N1>> );
static_assert( std::is_same_v<std::index_sequence<Js...>,
std::make_index_sequence<N2>> );
static_assert( std::is_same_v<std::index_sequence<Ks...>,
std::make_index_sequence<N1*N2>> );
// ...
};
我希望能够在构造函数的初始化列表中使用参数包扩展。实现此目标的最佳方法是赋予我的 class 参数包模板参数吗?这是我的意思的一个例子: https://coliru.stacked-crooked.com/a/e699c4cd035e0b1c
#include <utility>
#include <iostream>
template<typename T, std::size_t...Is>
struct base_vec
{
constexpr static std::size_t N = sizeof...(Is);
T e[N];
base_vec() : e{} {}
explicit base_vec(const T& s) : e{((void)Is,s)...} {}
};
template<typename T, std::size_t...Is>
std::ostream& operator<<(std::ostream& lhs, const base_vec<T,Is...>& rhs)
{
return (std::cout << ... << rhs.e[Is]);
}
template<typename T, std::size_t...Is>
constexpr auto getVecIs(std::index_sequence<Is...> seq)
{
return base_vec<T, Is...>{};
}
template<typename T, std::size_t N>
using vec = decltype(getVecIs<T>(std::declval<std::make_index_sequence<N>>()));
int main()
{
vec<int,3> v(2);
std::cout << v << "\n";
return 0;
}
注意微不足道的扩展e{((void)Is,s)...}
。这是一个好的做法,还是我错过了这种方法的一些缺点(除了现在我的结构将有一个完整的参数包,而不是单个 size_t N
)?
您可以将扩展完全移入 class:
template<typename T, std::size_t N>
struct vec
{
public:
T e[N];
vec() : e{} {}
explicit vec(const T& s) : vec{s, std::make_index_sequence<N>{}} {}
private:
template <std::size_t ... Is>
vec(const T& s, std::index_sequence<Is...>) : e{(Is, s)...} {}
};
在我看来,您正在重新创建一个 class 灵感来自 std::array
的一个方便的索引序列来操作。
有趣。
我建议直接定义 vec
的简化(恕我直言),避免 base_vec
,使用模板专业化
#include <utility>
#include <iostream>
template <typename T, std::size_t N, typename = std::make_index_sequence<N>>
struct vec;
template <typename T, std::size_t N, std::size_t...Is>
struct vec<T, N, std::index_sequence<Is...>>
{
T e[N] {} ;
vec (const T& s) : e{((void)Is,s)...}
{ }
};
template <typename T, std::size_t...Is>
std::ostream& operator<< (
std::ostream& lhs,
vec<T, sizeof...(Is), std::index_sequence<Is...>> const & rhs)
{ return (std::cout << ... << rhs.e[Is]); }
int main ()
{
vec<int, 3u> v(2);
std::cout << v << "\n";
}
您可以在 vec
专业的 body 中添加一个 static_assert()
如下
static_assert( N == sizeof...(Is) );
避免"hijacked"使用如下
vec<int, 5u, std::make_index_sequence<12u>> bad_vec_1;
或者,也许更好,
static_assert( std::is_same_v<std::index_sequence<Is...>,
std::make_index_sequence<N>> );
避免aslo
vec<int, 5u, std::index_sequence<1u, 3u, 5u, 2u, 4u>> bad_vec_2;
-- 编辑--
OP 询问
Any ideas how to extend this to multiple index sequences - for multi-dimensional arrays for example?
很简单。
根据 Jarod42 的建议,您可以添加另一个 N
和另一个 std::index_sequence
。
对于 2 dim 的情况,我建议如下。
我还为 N1*N2
添加了第三个索引序列(我想可能会有用)。
我把专业化的body留作练习。
template <typename T, std::size_t N1, std::size_t N2,
typename = std::make_index_sequence<N1>,
typename = std::make_index_sequence<N2>,
typename = std::make_index_sequence<N1*N2>>
struct vec2dim;
template <typename T, std::size_t N1, std::size_t N2,
std::size_t ... Is, std::size_t ... Js, std::size_t ... Ks>
struct vec2dim<T, N1, N2, std::index_sequence<Is...>,
std::index_sequence<Js...>, std::index_sequence<Ks...>>
{
static_assert( std::is_same_v<std::index_sequence<Is...>,
std::make_index_sequence<N1>> );
static_assert( std::is_same_v<std::index_sequence<Js...>,
std::make_index_sequence<N2>> );
static_assert( std::is_same_v<std::index_sequence<Ks...>,
std::make_index_sequence<N1*N2>> );
// ...
};