1

I just started to look into SFINAE to get an idea of what it is. I am following this tutorial and mostly its making sense. but I am confused where the crucial part of the code is.

within the second enable_if template argument list. there is a false and a template type T argument.

template <class T>
struct enable_if<false, T>
{};

why is the false is used here? What does the false evaluate to when the template instantiation is encountered in the code? if there is a term for such template argument types I would be happy if you could share the term.

Here is the full code -

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

template <typename T>
class is_container
{
  typedef char true_type;
  struct false_type { true_type _[2]; }; //false_type must be larger then the true_type. 
  template <typename U>
  static true_type has_iterator_checker(typename U::iterator *); //overload 1,.called if the type passed contains an iterator.  and returns a true type.
  template <typename U>
  static false_type has_iterator_checker(...); //if false type is passed //overload 2, called if the type passed DOESN'T contain an iterator. and returns a false type.
public:
  enum
  {
    value = (sizeof(has_iterator_checker<T>(0)) == sizeof(true_type)) //compares the size of the overlad with the true type. if they dont match, value becames false.
  };
};

template <bool Cond, class T = void> //first param is is a bool condition, second param is the return type. 
struct enable_if
{
  typedef T type; //if bool Cond is true, it returns the type.
};

//if bool cond is false, appereantly the following template does nothing and compiler silently fails without throwing error.  
//it seems this is what makes SFINAE a powerfull tool.
//but what i dont understand is that. <false, T>. there is no bool type before false. so what does this false evaluate into whenever compiler
//encounters  it the code?
template <class T>
struct enable_if<false, T>
{};

template <typename T>
typename enable_if<!is_container<T>::value>::type
super_print(T const &t)
{
  std::cout << t << std::endl;
}

template <typename T>
typename enable_if<is_container<T>::value>::type
super_print(T const &t)
{
  typedef typename T::value_type value_type;
  std::copy(t.begin(),
    t.end(),
    std::ostream_iterator<value_type>(std::cout, ", "));
  std::cout << std::endl;
}

int main()
{
  super_print(10); // a condition that is false.
  std::vector<int> b;
  b.push_back(1);
  b.push_back(2);
  b.push_back(3);
  super_print(b);

  return 0;
}
Yucel_K
  • 688
  • 9
  • 17

1 Answers1

4
template <class T>
struct enable_if<false, T>
{};

Is a partial specialization for the enable_if class and it is used as a dead end. When you have

template <typename T>
typename enable_if<!is_container<T>::value>::type
super_print(T const &t)
{
  std::cout << t << std::endl;
}

The compiler evaluates !is_container<T>::value. If it is true then

template <bool Cond, class T = void> //first param is is a bool condition, second param is the return type. 
struct enable_if
{
  typedef T type; //if bool Cond is true, it returns the type.
};

is the enable_if used and type becomes void because you didn't specify anything in

typename enable_if<!is_container<T>::value>::type

Now, if it evaluates to false then

template <class T>
struct enable_if<false, T>
{};

is the enable_if that is chosen since we said this is the one to use when the bool part of the template is false and it doesn't have a type member. That means that

typename enable_if<!is_container<T>::value>::type

is a substitution failure, since there is no type member. Since substitution failure is not an error, the compiler just removes then function from the overload set.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402