正确使用std :: enable_if作为返回类型

时间:2017-11-07 07:22:24

标签: c++ c++11 templates sfinae

我运行f<5>()并获取如下所示的错误日志:

template<bool B, typename T>
using Enable_if = typename std::enable_if<B,T>::type;

template<int N>
Enable_if<(N>0),void> f(){
    std::cout << N;
    f<N-1>();
}

template<int N>
Enable_if<(N==0),void> f(){
 return;   
}


In instantiation of 'Enable_if<(N > 0), void> f() [with int N = 1; Enable_if<(N > 0), void> = void]':
13:12:   recursively required from 'Enable_if<(N > 0), void> f() [with int N = 3; Enable_if<(N > 0), void> = void]'
13:12:   required from 'Enable_if<(N > 0), void> f() [with int N = 4; Enable_if<(N > 0), void> = void]'
25:8:   required from here
13:12: error: no matching function for call to 'f()'
13:12: note: candidate is:
11:23: note: template<int N> Enable_if<(N > 0), void> f()
11:23: note:   template argument deduction/substitution failed:

下面的代码与f<5>()一起正常运行并打印543210:

template<int N>
void f(){
    std::cout << N;
    f<N-1>();
}

template<>
void f<0>(){
    std::cout << 0;  
}

取自Stroustrup(C ++ 11)第845页。

2 个答案:

答案 0 :(得分:3)

第一种情况是功能模板重载。调用f<1>()时,将调用第一个重载;在其中调用f<0>()。此时,找不到匹配的功能;第一次重载仅在N > 0时实例化,之后宣布第二次重载,然后才能找到。

如果您在第一个过载之前移动第二个过载,则代码可以正常工作。 LIVE

第二种情况是具有完整模板专业化的功能模板。对于f<1>(),将调用主模板;在其中调用f<0>()。然后名称查找仍然会找到主模板本身;之后检查专业化,然后为f<0>()调用专业化版本。这意味着如果N == 0没有专门化,它将导致递归模板实例化。 LIVE

答案 1 :(得分:0)

第一件事是当用T == 1实例化第一个模板函数时尝试实例化f<0>,但是在那时还没有声明第二个模板函数,所以编译器只尝试实例化第一个模板函数并且在没有这样做之后停止编译。要解决此问题,您需要在使用之前添加第二个模板函数的声明,如果第一个模板函数(也Enable_if不是必需的,因为标准库中有std::enable_if_t可以执行完全相同的操作):

#include <iostream>
#include <type_traits>

// Forwrad declaration.
template<int N> ::std::enable_if_t<(0 == N)>
f(void);

template<int N> ::std::enable_if_t<(0 < N)>
f(void)
{
    ::std::cout << N;
    f<N-1>();
}

template<int N> ::std::enable_if_t<(0 == N)>
f(void)
{
    ::std::cout << "0, end";
}

int
main()
{
    f<5>();
    ::std::cout.flush();
    return(0);
}

Run this code in online compiler