Correctly using std::enable_if as a return type

Viewed 977

I run f<5>() and get the error log shown below:

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:

The code below runs fine with f<5>() and prints 543210:

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

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

It is taken from Stroustrup(C++11) page 845.

2 Answers

The 1st case is function template overloading. When f<1>() is called, the 1st overload will be invoked; inside which f<0>() is invoked. At that point, no matching function could be found; the 1st overload only instantiate when N > 0, the 2nd overload is declared afterwards then can't be found yet.

If you move the 2nd overload before the 1st one the code would work. LIVE

The 2nd case is function template with full template specialization. For f<1>(), the primary template will be invoked; inside which f<0>() is invoked. Then name lookup will still find the primary template itself; after that the specializations are checked then for f<0>() the specialization version will be called. That means if there's no specialization for N == 0 it will cause recursive template instantiation. LIVE

The first thing is that when instantiated with T == 1 the first template function in try to instantiate f<0>, however at that point a second template function is not yet declared so compiler only tries instantiated first template function and stops compilation after fails to do so. To fix this you need to add declaration of second template function prior to its use if first template function (Also Enable_if is not necessary because there is std::enable_if_t available in standard library that does exactly the same thing):

#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

Related