创建具有扩展索引序列的结构

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>> );
   // ...
 };