Search code examples
c++functiontemplatessfinaeenable-if

C++ template function implementation is not correct


I've implemented this function here (in a single header file).

//header.h
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <stdint.h>
#include <type_traits>



   template < typename T,
           typename = std::enable_if<std::is_integral<T> >::type >
void f(T t) {
    printf("Integer function\n");
}

template void f(int i);

The main is below...

#include "header.h"
int main(int argc, char** argv) {
    int p = 3;
    f(p);
}

It compiles... no problem, but it doesn't show the content of the printf, what am i missing?... (i'm using eclipse as IDE, it could be a problem?)

One more thing... does it make sense this implementation? (i would like to write different code based on the type in input, whether it is unsigned or not).

template < typename T,
           typename = std::enable_if<std::is_integral<T>::value>,
           typename = std::enable_if<std::is_unsigned<T>::value>
>
void f(T t) {
    printf("Unsigned f version.\n");
}

template < typename T,
           typename = std::enable_if<std::is_integral<T>::value>,
           typename = std::enable_if<std::is_signed<T>::value>
>
void f(T t) {
    printf("Signed f version.\n");
}

In this case it doesn't compile... so how to deal with this?

Thank you

Update...

I tried the modification suggested in the first code, it gives me the following error

..\header.h:19:58: error: type/value mismatch at argument 1 in template parameter list for 'template<bool <anonymous>, class _Tp> struct std::enable_if'
            typename = std::enable_if<std::is_integral<T> >::type >
                                                          ^
..\header.h:19:58: error:   expected a constant of type 'bool', got 'std::is_integral<_Tp>'
..\header.h:19:61: error: expected '>' before 'type'
            typename = std::enable_if<std::is_integral<T> >::type >
                                                             ^
..\main.cc: In function 'int main(int, char**)':

Update 2...

Ok guys... I've been trying to figure out what it is i do not understand... so i'm digging into the code of type_traits (so my goal is to understand what i've done wrong...)

I report the code for the enable_if (from type_traits).

template<bool, typename _Tp = void>
    struct enable_if //Lukkio decl. 1
    { };

  // Partial specialization for true.
  template<typename _Tp>
    struct enable_if<true, _Tp> //Lukkio decl. 2
    { typedef _Tp type; };

Because i want to understand what happens to a line like:

template<
    typename T,
    typename = std::enable_if<std::is_integral<T>::value>
>
void function(T t) {
    printf("Is integral!\n");
}

So assuming T is fixed (let's say int, which is integrale) the std::enable_if::value> use the decl. 1, assuming however the _Tp is of type void so actually what happens, in terms of reduction (i now substitute to T the keyword int... should be something like

template<
        typename int,
        typename = std::enable_if<std::is_integral<int>::value>
    >

namely

template<
        typename int,
        typename = std::enable_if<1>
    >

So my question is... what does the second typename stands for?


Solution

  • SFINAE on default function template arguments doesn't really scale. You can only use this if you want to restrict a single overload to a particular class of types.

    For non-overlapping multiple overloads (like your signed/unsigned integrals), you can SFINAE on the return type like this:

    #include <cstdio>
    #include <type_traits>
    
    template < typename T>
    auto f(T t) 
        -> std::enable_if_t<std::is_unsigned<T>::value && std::is_integral<T>::value>
    {
        printf("Unsigned f version.\n");
    }
    
    template < typename T>
    auto f(T t) 
        -> std::enable_if_t<std::is_signed<T>::value && std::is_integral<T>::value>
    {
        printf("Signed f version.\n");
    }
    
    int main()
    {
        unsigned u = 1;
        signed s = 1;
        f(u);
        f(s);
    }
    

    Live Example

    Note: the enable_if_t<T> is a type alias for typename enable_if<T>::type that came available in C++14 (you can write it yourself)