Search code examples
template-meta-programmingsfinaeenable-if

conditional compilation of void argument member method using enable_if


.

#include <iostream>
#include <type_traits>

using namespace std;

template<typename T>
struct MyClass{

  void hello( void) {
    hello(std::is_same<T,bool>());
  }

   void hello(std::true_type){
     cout<<"hello only for bools"<<endl;
   }

};


int main(int argc, char** argv){

  MyClass<bool> myclass1;
  myclass1.hello();

  MyClass<float> myclass2;
  //myclass2.hello(); //throws error as it should

  return 0;
}

I wrote the above code after reading enable_if method specialization. I want the hello() method to exist only if template argument is bool and it works. However I am running into problems when I am trying to solve the same problem using enable_if. I have the following code. Any help is appreciated. If enable_if is not appropriate for this job, what is generally used?

#include <iostream>
#include <type_traits>

using namespace std;

template<typename T>
struct MyClass{

  typename std::enable_if<std::is_same<T,bool>::value, void>::type
  hello(void)
  {
    cout<<"hello only for bools"<<endl;
  }
};

int main(int argc, char** argv){

  MyClass<bool> myclass1;
  myclass1.hello();

  MyClass<float> myclass2;// compilation error. Don't know how to solve
  //myclass2.hello(); //I want only this line to cause compilation error

  return 0;
}

EDIT: I found the solution to my question in jpihl's answer at std::enable_if to conditionally compile a member function. But could anyone explain why the above doesn't work?

#include <iostream>
#include <type_traits>

using namespace std;

template<typename T>
struct MyClass{

  template<class Q = T>
  typename std::enable_if<std::is_same<Q, bool>::value, void>::type hello()
  {
    cout<<"hello only for bools"<<endl;
  }

};

int main(int argc, char** argv){

  MyClass<bool> myclass1;
  myclass1.hello();

  MyClass<float> myclass2;// throws errow. Don't know how to solve
  myclass2.hello(); //

  return 0;
}

Solution

  • Your first attempt with enable_if does not work because SFINAE applies in overload resolution of function (or member function) templates, where it will eliminate a specialization of the function template from the overload set when that specialization cannot compile.

    The member hello, in your first attempt, is not a member function template. It has no template parameters. It is simply a member function of a class template.

    Its return type is formulated by an enable_if expression that will provoke compilation failure if the class template parameter T is not instantiated as bool. This doesn't make the member function itself into a template. SFINAE has no application. Once you declare MyClass<float> myclass2, the specialization of MyClass<T> and all its members is completely determined. The member function hello of that specialization must be instantiated, and with T = float the attempt to do so must fail to compile.

    In the second, successful attempt, hello is a member function template (of a class template). It has a template parameter, Q, which by default is = T. So SFINAE applies and you can leverage it with enable_if in the intended manner. You may declare MyClass<float> myclass2 without error, because doing so does not force any instantiation of the template member MyClass<float>::hello<Q>

    Since you have written only one overload of hello, there is only one specialization of the member function template for any choice of Q. When Q = bool, that single specialization survives and myclass1.hello() will compile. When Q != bool, SFINAE eliminates the that single specialization and myclass2.hello() does not compile.

    To appeciate vividly how SFINAE in the second case is operating at instantation of the member function template, consider that:

      MyClass<float> myclass2;
      myclass2.hello<bool>();
    

    is fine; while on the other hand:

      MyClass<bool> myclass1;
      myclass1.hello<float>();
    

    does not compile.

    Here is documentation of SFINAE