我正在尝试用 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>
。我不知道如何实现。
最佳答案
首先,让我们从返回类型推导中拆分 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 找到的任何函数。
这个集合不包括我们自己。
因此我们将创建一个 details 命名空间,并按如下方式强制 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...>;
请注意,我将所有内容都作为参数传递,从中推导出模板参数。这允许 adl 正常工作。
另一种方法是在没有辅助函数的情况下直接构造类型;我假设您出于自己的原因使用函数来构造类型。基于函数的模板元编程有很多优点;查看 boost hana 以了解您可以走多远。也有缺点,例如您必须通过 ADL 循环才能递归返回类型。
关于c++ - C++11 中递归元函数的延迟返回类型解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36632985/