Search code examples
c++c++11templatesenable-if

How to use std::enable_if on method of templated class with seperate declaration and definition via specialization


I'm trying to have a templated class split between a header file and implementation using specialization, but I want one method to only appear in some specializations.

Header file:

template <typename T>
class A
{
  public:
  void foo();
  void bar();

  template<typename U = T, typename std::enable_if<std::is_convertible<int,U>::value>::type* = nullptr>
  void special();
};

The implementation:

template<typename T>
void A<T>::foo()
{
  ...
}

template<typename T>
void A<T>::bar()
{
  ...
}

template<typename T, typename std::enable_if<std::is_convertible<int,T>::value>::type>
void A<T>::special()
{
  ...
}

// generate specializations
template
class A<float>;

template
class A<int>;

template
class A<std::string>;

However I keep getting error: declaration is incompatible with function template "void A<T>::special()" when I try it like this, or when I move the std::enable_if to be the return type. How should the definition be to match the declaration of this method special()?


Solution

  • There are a few issues with this code. In the declaration, you have an extra angle bracket. Also, * immediately followed by = will always be interpreted as a single token *=; they must be separated by a space in this code in order for the * to be interpreted as forming a pointer and = as declaring a default value for the template argument. Thus, the declaration should read:

    template<typename U = T,
             typename std::enable_if<std::is_convertible<int,U>::value>::type* = nullptr>
    void special();
    

    In the definition, since you are defining a member template of a class template, you need to write out the template parameter list for the class template first, then the member template. Also, you did not have enough template parameters in your definition: remember, U was also there, and it is U, not T, that needs to appear in the is_convertible part:

    template <typename T>
    template <typename U, typename std::enable_if<std::is_convertible<int,U>::value>::type*>
    void A<T>::special()
    {
        // ...
    }
    

    Note that the exact spelling of U here is not important: we are allowed to relabel U as it is a "dummy variable":

    template <typename T>
    template <typename V, typename std::enable_if<std::is_convertible<int,V>::value>::type*>
    void A<T>::special()
    {
        // ...
    }
    

    However, besides relabelling, all the other details must match exactly.