以 SFINAE 作为模板参数的编译时递归
Compile-time recursion with SFINAE as template argument
我一直在玩模板,我想用它们来编写一个递归函数,该函数将在编译时求值。我希望它根据我传递给它的数字进行分支。该函数确实有一个约束;我想保留 return 值。
所以这是我尝试编写的函数(无法编译):
template<int n, typename std::enable_if_t<n==1>>
constexpr auto fun() { return std::make_tuple(1); }
template<int n, typename std::enable_if_t<n==2>>
constexpr auto fun() { return std::make_tuple(fun<1>(), 2); }
template<int n, typename enable = void>
constexpr auto fun() {
return std::tuple_cat(fun<n-1>(), n);
}
int main() {
constexpr auto x = fun<4>();
return 0;
}
我面临的问题是我不确定将 std::enable_if_t
语句放在哪里,以及如何准确地编写它以确保我的函数正确分支。我在这里错过了什么?
我不清楚你到底想要什么,但是......让我猜猜:你想要的东西如下
#include <tuple>
#include <type_traits>
template<int n, std::enable_if_t<n==1, bool> = true>
constexpr auto fun() { return std::make_tuple(1); }
template<int n, std::enable_if_t<n!=1, bool> = true>
constexpr auto fun() { return std::tuple_cat(fun<n-1>(), std::make_tuple(n)); }
int main() { constexpr auto x = fun<4>(); }
诀窍
template<int n, typename enable = void>
是模板 class 的 SFINAE 技巧,其中有主要版本和一些专业化;所以主要版本定义了签名和专业化 SFINAE enabled/disabled 到 std::enable_if
.
对于函数,您有函数重载但不是偏特化。您必须 enable/disable 每个单独的重载函数
template<int n, std::enable_if_t<n==1, bool> = true>
// ...
template<int n, std::enable_if_t<n!=1, bool> = true>
// ...
注意我写了
template<int n, std::enable_if_t<n==1, bool> = true>
而不是
template<int n, typename = std::enable_if_t<n==1>>
第二种形式也适用于 enable/disable 单个函数,但当您有具有相同签名的不同函数(如您的情况)并且您只想启用一个版本时,则不起作用。
假设你想连接你的元组并以
的形式创建它
fun<4>() == tuple(1, 2, 3, 4);
你可以像
一样写两个模板
template<int n, std::enable_if_t<n == 1>* = nullptr>
constexpr auto fun()
{
return std::make_tuple(1);
}
template<int n, std::enable_if_t<n != 1>* = nullptr>
constexpr auto fun()
{
return std::tuple_cat(fun<n-1>(), std::tuple(n));
}
然而,这不是 "C++17 way" 的做法。用if constexpr
可以表达得更好
template<int n>
constexpr auto fun2()
{
if constexpr (n > 1)
return std::tuple_cat(fun2<n-1>(), std::tuple(n));
else
return std::tuple(1);
}
关于序列创建(如1, 2, 3, 4, ...
)还有std::integer_sequence
可以与模板参数包结合使用
template <int... nums>
constexpr auto construct(std::integer_sequence<int, nums...>)
{
return std::tuple((nums + 1)...);
}
template<int n>
constexpr auto fun3()
{
return construct(std::make_integer_sequence<int, n>());
}
Here 是一个完整的例子。
我一直在玩模板,我想用它们来编写一个递归函数,该函数将在编译时求值。我希望它根据我传递给它的数字进行分支。该函数确实有一个约束;我想保留 return 值。
所以这是我尝试编写的函数(无法编译):
template<int n, typename std::enable_if_t<n==1>>
constexpr auto fun() { return std::make_tuple(1); }
template<int n, typename std::enable_if_t<n==2>>
constexpr auto fun() { return std::make_tuple(fun<1>(), 2); }
template<int n, typename enable = void>
constexpr auto fun() {
return std::tuple_cat(fun<n-1>(), n);
}
int main() {
constexpr auto x = fun<4>();
return 0;
}
我面临的问题是我不确定将 std::enable_if_t
语句放在哪里,以及如何准确地编写它以确保我的函数正确分支。我在这里错过了什么?
我不清楚你到底想要什么,但是......让我猜猜:你想要的东西如下
#include <tuple>
#include <type_traits>
template<int n, std::enable_if_t<n==1, bool> = true>
constexpr auto fun() { return std::make_tuple(1); }
template<int n, std::enable_if_t<n!=1, bool> = true>
constexpr auto fun() { return std::tuple_cat(fun<n-1>(), std::make_tuple(n)); }
int main() { constexpr auto x = fun<4>(); }
诀窍
template<int n, typename enable = void>
是模板 class 的 SFINAE 技巧,其中有主要版本和一些专业化;所以主要版本定义了签名和专业化 SFINAE enabled/disabled 到 std::enable_if
.
对于函数,您有函数重载但不是偏特化。您必须 enable/disable 每个单独的重载函数
template<int n, std::enable_if_t<n==1, bool> = true>
// ...
template<int n, std::enable_if_t<n!=1, bool> = true>
// ...
注意我写了
template<int n, std::enable_if_t<n==1, bool> = true>
而不是
template<int n, typename = std::enable_if_t<n==1>>
第二种形式也适用于 enable/disable 单个函数,但当您有具有相同签名的不同函数(如您的情况)并且您只想启用一个版本时,则不起作用。
假设你想连接你的元组并以
的形式创建它fun<4>() == tuple(1, 2, 3, 4);
你可以像
一样写两个模板template<int n, std::enable_if_t<n == 1>* = nullptr>
constexpr auto fun()
{
return std::make_tuple(1);
}
template<int n, std::enable_if_t<n != 1>* = nullptr>
constexpr auto fun()
{
return std::tuple_cat(fun<n-1>(), std::tuple(n));
}
然而,这不是 "C++17 way" 的做法。用if constexpr
template<int n>
constexpr auto fun2()
{
if constexpr (n > 1)
return std::tuple_cat(fun2<n-1>(), std::tuple(n));
else
return std::tuple(1);
}
关于序列创建(如1, 2, 3, 4, ...
)还有std::integer_sequence
可以与模板参数包结合使用
template <int... nums>
constexpr auto construct(std::integer_sequence<int, nums...>)
{
return std::tuple((nums + 1)...);
}
template<int n>
constexpr auto fun3()
{
return construct(std::make_integer_sequence<int, n>());
}
Here 是一个完整的例子。