序列元组

Tuple of sequence

本文关键字:元组      更新时间:2023-10-16

我想将一个类序列存储到一个元组中,并将该序列声明为另一个类的成员:

template<size_t id> class Foo {};
template<size_t N>
class FooContainer
{
  std::tuple<Foo<0>, Foo<1>, ..., Foo<N>> tup; // build this type at compile time ??
};

我试过了:

template<size_t N>
class FooContainer
{
  template<size_t... id>
  struct FoosImpl {
    constexpr FoosImpl(std::index_sequence<id...>) {};
    using type = std::tuple<Foo<id>...>;
  };
  template<size_t N, typename Indices = std::make_index_sequence<N>>
  using Foos = decltype(FoosImpl(Indices())::type);
  Foos<N> tup;
};

但是这不会编译。GCC抱怨道:error: missing template arguments before ‘(’ token using Foos = decltype(FoosImpl(Indices())::type);

我认为编译器不需要指定模板,它会从Indices()推断出整数序列。但事实似乎并非如此。

这是你想要做的一种可能的方法:

#include <tuple>
template<size_t id> class Foo {};
template <size_t... Idx>
std::tuple<Foo<Idx>...> get_foos(std::index_sequence<Idx...>);
template <size_t N>
using foo_tuple = decltype(get_foos(std::make_index_sequence<N>{}));
template<size_t N>
class FooContainer {
     foo_tuple<N> tup;
};

你不能(目前)让编译器推断类模板参数(就像你在FoosImpl中需要的那样),但你可以让它推断函数的模板参数并使用返回类型。

从您的示例开始,您可以这样做:

#include<tuple>
#include<utility>
#include<type_traits>
template<size_t id> class Foo {};
template<size_t N>
struct FooContainer
{
  template<size_t... id>
  static constexpr std::tuple<Foo<id>...> func(std::index_sequence<id...>) {}
  using Foos = decltype(func(std::make_index_sequence<N>()));
  Foos foos;
};
int main() {
  static_assert(std::is_same<FooContainer<3>::Foos, std::tuple<Foo<0>, Foo<1>, Foo<2>>>::value, "!");
}

或者您可以简单地使用默认值为std::index_sequence<0,...,N>的FooContainer的附加参数,如:

#include <utility>
#include <tuple>
#include <iostream>
#include <typeinfo>
template<size_t I> class Foo {};
template <size_t N, class = std::make_index_sequence<N>>
struct FooContainer;
template <size_t N, size_t... Is>
struct FooContainer<N, std::index_sequence<Is...>> {
   using Foos = std::tuple<Foo<Is>...>;
   Foos foos;
};
int main() {
   std::cout << typeid(FooContainer<3>{}.foos).name() << std::endl;
}
输出:

$ g++ -std=c++14 example.cc
$ ./a.out
St5tupleII3FooILm0EES0_ILm1EES0_ILm2EEEE
$ c++filt -t St5tupleII3FooILm0EES0_ILm1EES0_ILm2EEEE
std::tuple<Foo<0ul>, Foo<1ul>, Foo<2ul> >
编辑:

正如skypjack提到的,现在可以通过显式传递第二个参数以意想不到的方式使用我们的FooContainer类…如果不希望在FooContainer的第二个参数中传递任意序列,可以通过添加static_assert来保护代码,如下所示:

#include <utility>
#include <tuple>
#include <iostream>
#include <typeinfo>
#include <type_traits>
template<size_t I> class Foo {};
template <size_t N, class = std::make_index_sequence<N>>
struct FooContainer;
template <size_t N, size_t... Is>
struct FooContainer<N, std::index_sequence<Is...>> {
   static_assert(std::is_same<std::make_index_sequence<N>, std::index_sequence<Is...>>::value, "passed index_sequence was not generated using std::make_index_sequence<N>");
   using Foos = std::tuple<Foo<Is>...>;
   Foos foos;
};
int main() {
   std::cout << typeid(FooContainer<3>{}.foos).name() << std::endl;
}