home | help
std::enable_if(3)	       C++ Standard Libary	       std::enable_if(3)

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  prior  to
     C++20's concepts,
	in  particular	for  conditionally removing functions from the candidate
     set based on
	type traits, allowing separate	function  overloads  or  specializations
     based on those
	different type traits.

	std::enable_if can be used in many forms, including:

	  *  as an additional function argument (not applicable to most operator
     overloads),
	  * as a return type (not applicable to constructors and destructors),
	  * as a class template or function template parameter.

	If the program adds specializations for std::enable_if, the behavior  is
     undefined.

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 does not work because the  declarations
     are treated
	as  redeclarations of the same function template (default template argu-
     ments are not
	accounted for in function template equivalence).

      /* WRONG */

      struct T
      {
	  enum { int_t, float_t } type;

	  template<typename Integer,
		   typename	 =	 std::enable_if_t<std::is_integral<Inte-
     ger>::value>>
	  T(Integer) : type(int_t) {}

	  template<typename Floating,
		   typename   =   std::enable_if_t<std::is_floating_point<Float-
     ing>::value>>
	  T(Floating) : type(float_t) {} // error: treated as redefinition
      };

      /* RIGHT */

      struct T
      {
	  enum { int_t, float_t } type;

	  template<typename Integer,
		   std::enable_if_t<std::is_integral<Integer>::value,  bool>   =
     true>
	  T(Integer) : type(int_t) {}

	  template<typename Floating,
		   std::enable_if_t<std::is_floating_point<Floating>::value,
     bool> = true>
	  T(Floating) : 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 specifica-
     tions 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  dis-
     tinct
	templates.  Nonetheless, #2 and #4, despite being instantiations of dif-
     ferent
	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 <iostream>
      #include <new>
      #include <string>
      #include <type_traits>

      namespace detail
      {
	  void*   voidify(const   volatile   void*   ptr)   noexcept   {  return
     const_cast<void*>(ptr); }
      }

      // #1, enabled via the return type
      template<class T>
      typename			   std::enable_if<std::is_trivially_default_con-
     structible<T>::value>::type
	  construct(T*)
      {
	  std::cout  <<  "default  constructing  trivially default constructible
     T\n";
      }

      // same as above
      template<class T>
      typename			  std::enable_if<!std::is_trivially_default_con-
     structible<T>::value>::type
	  construct(T* p)
      {
	  std::cout << "default constructing non-trivially default constructible
     T\n";
	  ::new(detail::voidify(p)) T;
      }

      // #2
      template<class T, class... Args>
      std::enable_if_t<std::is_constructible<T,   Args&&...>::value>   //  Using
     helper type
	  construct(T* p, Args&&... args)
      {
	  std::cout << "constructing T with operation\n";
	  ::new(detail::voidify(p)) T(static_cast<Args&&>(args)...);
      }

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

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

      // #5, enabled via a type 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_float-
     ing_point<T>::value>::type>
      {}; // specialization for floating point types

      int main()
      {
	  union { int i; char s[sizeof(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>{}; // OK: matches the primary template
	  A<double>{}; // OK: matches the partial specialization
      }

Output:
      default constructing trivially default constructible T
      destroying trivially destructible T
      constructing T with operation
      destroying non-trivially destructible T

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

	  * static_assert
	  * SFINAE
	  * Constraints and Concepts

http://cppreference.com 	   2024.06.10		       std::enable_if(3)

home | help