以 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 是一个完整的例子。