C ++ 11中递归元函数的延迟返回类型解析

时间:2016-04-14 20:08:36

标签: c++ templates c++11 template-meta-programming

我正在尝试在C ++ 11中编写一个简单的序列生成器,它需要一些启动和启动。结束索引并在编译时生成一个整数序列。由于integer_sequence在C ++ 14之前不可用,我使用自定义的可变参数模板化容器来存储整数序列:

template <int...>
struct IntSequence {};

现在我想调用一个sequence_generator函数,它返回一个我请求的整数范围内模板化的IntSequence容器。

auto t = sequence_generator<3, 5>();

我希望t成为IntSequence<3,4,5>。我递归地展开序列生成器并使用enable_if来停止递归:

template <int S, int E, int... Seq>
auto sequence_generator() -> enable_if_t<(S <= E), IntSequence<Seq...>> {
// Pack S into the variadic sequence and increment to S+1.  
return sequence_generator<S + 1, E, S, Seq...>();
}

template <int S, int E, int... Seq>
auto sequence_generator() -> enable_if_t<!(S <= E), IntSequence<Seq...>> {
  return IntSequence<Seq...>{};
}

然而,我的enable_if的构造函数不对,因为我使用当前递归步骤提供的可变参数包来声明返回类型。相反,我希望编译器展开整个递归链并选择基本情况的返回类型(这将是IntSequence<3,4,5>。我不知道如何实现它。

3 个答案:

答案 0 :(得分:3)

首先,让我们从退货类型扣除中分割SFINAE

template <int S, int E, int... Seq, std::enable_if_t<(S <= E), int>* =nullptr>
auto sequence_generator() -> ???

接下来,函数的主体应该只是return {};

template <int S, int E, int... Seq, std::enable_if_t<(S <= E),int>* =nullptr>
auto sequence_generator() -> ???
{
  return {};
}

因为为什么要重复自己。

我们在合成返回类型时可见的函数是在此函数模板之前声明的以及通过ADL找到的任何函数。

这套不包括我们自己。

因此,我们将创建详细信息名称空间,并按如下方式强制ADL:

namespace details {
  struct helper {};

  template <int S, int E, int... Seq, std::enable_if_t<(S <= E), int>* =nullptr>
  auto sequence_generator(helper, IntSequence<S,E,Seq...>)
  -> ???
  {
    return {};
  }
  ???
}
template<int S, int E, int...Seq>
auto sequence_generator()
-> decltype( sequence_generator(details::helper{}, IntSequence<S,E,Seq...>{}) )
{
  return {};
}

有趣地消除了为details::sequence_generator实际拥有实体的需要。

namespace details {
  struct helper {};

  template <int S, int E, int... Seq, std::enable_if_t<(S <= E), int>* =nullptr>
  auto sequence_generator(helper, IntSequence<S,E,Seq...>)
  -> ???

  template <int S, int E, int... Seq, std::enable_if_t<(S > E), int>* =nullptr>
  auto sequence_generator(helper, IntSequence<S,E,Seq...>)
  -> ???
}
template<int S, int E, int...Seq>
auto sequence_generator()
-> decltype( sequence_generator<S,E,Seq...>(helper::details{}) )
{
  return {};
}

我们现在必须实现这两个功能。

  template <int S, int E, int... Seq, std::enable_if_t<(S <= E), int>* =nullptr>
  auto sequence_generator(helper, IntSequence<S,E,Seq...>)
  -> decltype( sequence_generator(helper{}, IntSequence<S + 1, E, S, Seq...>{}) );

  template <int S, int E, int... Seq, std::enable_if_t<(S > E), int>* =nullptr>
  auto sequence_generator(helper, IntSequence<S,E,Seq...>)
  -> IntSequence<Seq...>;

and done (live example).

请注意,我将所有内容作为参数传递,从中推导出模板参数。这样可以让adl正常工作。

另一种方法是直接构造没有辅助函数的类型;我假设您正在使用函数来构造类型,这是出于您自己的原因。基于功能的模板元编程有一些优点;看看提升hana,看看你能走多远。还有一些缺点,比如必须通过ADL箍来递归返回类型。

答案 1 :(得分:1)

以下是如何推导整数序列的实际类型:

#include <cstddef>

template <size_t... Ints> struct integer_sequence { };

template <size_t begin, size_t end, size_t... ints>
struct integer_sequence_type { 
  using type = typename integer_sequence_type<begin+1, end, ints..., begin>::type;
};

template <size_t begin, size_t... ints>
struct integer_sequence_type<begin, begin, ints...> { 
  using type = integer_sequence<ints..., begin>;
};

// below part is for verification - compiler will tell you exactly the type :)
void foo(int );
void foo(char );

void test() {
  foo(integer_sequence_type<4, 8>::type());
}

现在您可以使用此构造来确定函数的返回类型。 (注意:我没有查看你的功能,因为你没有要求它)

答案 2 :(得分:0)

最好的方法是从普通的index_sequence实现开始,例如从here开始并从序列转换:

template<class Seq, std::size_t Offset>
struct transform_seq;

template <std::size_t... Seq, std::size_t Offset>
struct transform_seq<seq<Seq...>, Offset>
{
    using type = seq<Seq+Offset...>;
};

template<std::size_t From, std::size_t To>
using sequence_generator = typename transform_seq<GenSeq<To-From+1>, From>::type;

结果可见here