Search code examples
c++c++11templatessfinaeclass-template

std::enable_if is generating error when used on functions of same name inside a template class


Problem description

I am trying to call a function that returns 1 or 2 depending on whether the type is signed char or unsigned int.

For this purpose, I wrote the following code. If I compile the code without the code inside the main. I get no compilation error.

But when I compile the code with the instantiation of an object Coverage, I get the following compilation errors:

main.cpp: In instantiation of ‘class Coverage<unsigned char>’:
<span class="error_line" onclick="ide.gotoLine('main.cpp',27)">main.cpp:27:28</span>:   required from here
main.cpp:12:9: error: no type named ‘type’ in ‘struct std::enable_if’
     int getNb(typename std::enable_if<std::is_signed<T>::value, void>::type) {
         ^~~~~
main.cpp:17:9: error: invalid parameter type ‘std::enable_if::type {aka void}’
     int getNb(typename std::enable_if<std::is_unsigned<T>::value, void>::type) {
         ^~~~~
main.cpp:17:9: error: in declaration ‘int Coverage::getNb(typename std::enable_if::value, void>::type)’
main.cpp: In function ‘int main()’:
main.cpp:28:18: error: ‘class Coverage’ has no member named ‘getNb’
   std::cout << c.getNb() << std::endl;

I understood that when we add typename std::enable_if as function argument it's not taken into consideration. It is also the only way to use when we have member functions with the same name.


Source Code

#include <iostream>
#include <type_traits>

template<typename T>
class Coverage
{
public:
   Coverage(T type) :_type(type) {}

   // signed char 
   int getNb(typename std::enable_if<std::is_signed<T>::value, void>::type) {
      return 1;
   }

   // unsigned int
   int getNb(typename std::enable_if<std::is_unsigned<T>::value, void>::type) {
      return 2;
   }

private:
   T _type;
};

int main()
{
   Coverage<unsigned char> c('c');
   std::cout << c.getNb() << std::endl;
   return 0;
}

Solution

  • Like @Evg mentioned you have to need the member function to be templated as well. In addition, you need to provide default values for them, like usual nullptr defaulting.

    See a demo

    #include <iostream>
    #include <type_traits>
    
    template<typename T>
    class Coverage {
    public:
       Coverage(T type) :_type(type) {}
    
       //signed char 
       template<typename Type = T> // templated the member!
       int getNb(typename std::enable_if<std::is_signed<Type>::value, void>::type* = nullptr) 
       {
          return 1;
       }
    
       //unsigned int
       template<typename Type = T> // templated the member!
       int getNb(typename std::enable_if<std::is_unsigned<Type>::value, void>::type* = nullptr)
       {
          return 2;
       }
    };
    

    Or provide a trailing return SFINAE for each functions

    See a demo

    #include <iostream>
    #include <type_traits>
    
    template<typename T>
    class Coverage {
    public:
       Coverage(T type) :_type(type) {}
    
       template<typename Type = T>
       auto getNb() -> typename std::enable_if<std::is_signed<Type>::value, int>::type
       {
          return 1;
       }
    
    
       template<typename Type = T>
       auto getNb() -> typename std::enable_if<std::is_unsigned<Type>::value, int>::type
       {
          return 2;
       }
    
    private:
       T _type;
    };