序列元组

时间:2016-08-19 14:13:50

标签: c++

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

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;
};

但这并没有编译。海湾合作委员会抱怨: error: missing template arguments before ‘(’ token using Foos = decltype(FoosImpl(Indices())::type);

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

3 个答案:

答案 0 :(得分:3)

这是一种做你想做的事情的可能方式:

#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),但是你可以让它推导出函数的模板参数并使用返回类型。

答案 1 :(得分:3)

从您的示例开始,您可以执行此操作:

#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, "!");
}

答案 2 :(得分:3)

或者您只需使用其他参数到您的FooContainer,默认值为std::index_sequence<0,...,N>,如:

#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;
}