std::enable_if (3) - Linux Manuals

std::enable_if: std::enable_if

NAME

std::enable_if - std::enable_if

Synopsis


Defined in header <type_traits>
template< bool B, class T = void > (since C++11)
struct enable_if;


If B is true, std::enable_if has a public member typedef type, equal to T; otherwise, there is no member typedef.
This metafunction is a convenient way to leverage SFINAE to conditionally remove functions from overload_resolution based on type traits and to provide separate function overloads and specializations for different type traits. std::enable_if can be used as an additional function argument (not applicable to operator overloads), as a return type (not applicable to constructors and destructors), or as a class template or function template parameter.

Member types


Type Definition
type either T or no such member, depending on the value of B

Helper types


template< bool B, class T = void > (since C++14)
using enable_if_t = typename enable_if<B,T>::type;

Possible implementation


  template<bool B, class T = void>
  struct enable_if {};


  template<class T>
  struct enable_if<true, T> { typedef T type; };

Notes


A common mistake is to declare two function templates that differ only in their default template arguments. This is illegal because default template arguments are not part of function template's signature, and declaring two different function templates with the same signature is illegal.


  /*** WRONG ***/


  struct T {
      enum { int_t,float_t } m_type;
      template <typename Integer,
                typename = std::enable_if_t<std::is_integral<Integer>::value>
      >
      T(Integer) : m_type(int_t) {}


      template <typename Floating,
                typename = std::enable_if_t<std::is_floating_point<Floating>::value>
      >
      T(Floating) : m_type(float_t) {} // error: cannot overload
  };


  /* RIGHT */


  struct T {
      enum { int_t,float_t } m_type;
      template <typename Integer,
                std::enable_if_t<std::is_integral<Integer>::value, int> = 0
      >
      T(Integer) : m_type(int_t) {}


      template <typename Floating,
                std::enable_if_t<std::is_floating_point<Floating>::value, int> = 0
      >
      T(Floating) : m_type(float_t) {} // OK
  };


Care should be taken when using enable_if in the type of a template non-type parameter of a namespace-scope function template. Some ABI specifications like the Itanium ABI do not include the instantiation-dependent portions of non-type template parameters in the mangling, meaning that specializations of two distinct function templates might end up with the same mangled name and be erroneously linked together. For example:


  // first translation unit


  struct X {
      enum { value1 = true, value2 = true };
  };


  template<class T, std::enable_if_t<T::value1, int> = 0>
  void func() {} // #1


  template void func<X>(); // #2


  // second translation unit


  struct X {
      enum { value1 = true, value2 = true };
  };


  template<class T, std::enable_if_t<T::value2, int> = 0>
  void func() {} // #3


  template void func<X>(); //#4


The function templates #1 and #3 have different signatures and are distinct templates. Nonetheless, #2 and #4, despite being instantiations of different function templates, have the same mangled name in_the_Itanium_C++_ABI (_Z4funcI1XLi0EEvv), meaning that the linker will erroneously consider them to be the same entity.

Example


// Run this code


  #include <type_traits>
  #include <iostream>
  #include <string>


  namespace detail { struct inplace_t{}; }
  void* operator_new(std::size_t, void* p, detail::inplace_t) {
      return p;
  }


  // #1, enabled via the return type
  template<class T,class... Args>
  typename std::enable_if<std::is_trivially_constructible<T,Args&&...>::value>::type
      construct(T* t,Args&&... args)
  {
      std::cout << "constructing trivially constructible T\n";
  }


  // #2
  template<class T, class... Args>
  std::enable_if_t<!std::is_trivially_constructible<T,Args&&...>::value> //Using helper type
      construct(T* t,Args&&... args)
  {
      std::cout << "constructing non-trivially constructible T\n";
      new(t, detail::inplace_t{}) T(args...);
  }


  // #3, enabled via a parameter
  template<class T>
  void destroy(
      T* t,
      typename std::enable_if<
          std::is_trivially_destructible<T>::value
      >::type* = 0
  ){
      std::cout << "destroying trivially destructible T\n";
  }


  // #4, enabled via a template parameter
  template<class T,
           typename std::enable_if<
               !std::is_trivially_destructible<T>{} &&
               (std::is_class<T>{} || std::is_union<T>{}),
              int>::type = 0>
  void destroy(T* t)
  {
      std::cout << "destroying non-trivially destructible T\n";
      t->~T();
  }


  // #5, enabled via a template parameter
  template<class T,
   typename = std::enable_if_t<std::is_array<T>::value> >

  void destroy(T* t) // note, function signature is unmodified
  {
      for(std::size_t i = 0; i < std::extent<T>::value; ++i) {
          destroy((*t)[i]);
      }
  }
  /*
  template<class T,
   typename = std::enable_if_t<std::is_void<T>::value> >

  void destroy(T* t){} // error: has the same signature with #5
  */


  // the partial specialization of A is enabled via a template parameter
  template<class T, class Enable = void>
  class A {}; // primary template


  template<class T>
  class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
  }; // specialization for floating point types


  int main()
  {
      std::aligned_union_t<0,int,std::string> u;


      construct(reinterpret_cast<int*>(&u));
      destroy(reinterpret_cast<int*>(&u));


      construct(reinterpret_cast<std::string*>(&u),"Hello");
      destroy(reinterpret_cast<std::string*>(&u));


      A<int> a1; // OK, matches the primary template
      A<double> a2; // OK, matches the partial specialization
  }

Output:


  constructing trivially constructible T
  destroying trivially destructible T
  constructing non-trivially constructible T
  destroying non-trivially destructible T

See also


void_t void variadic alias template
        (alias template)
(C++17)


* static_assert
* SFINAE