Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages

  
 
  

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	candi-
       date 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 operator
       overloads)
	    * as a return type (not applicable to  constructors	 and  destruc-
       tors)
	    * as a class template or function template parameter

	  The behavior of a program that adds specializations for enable_if 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  declara-
       tions are treated
	  as  redeclarations  of  the same function template (default template
       arguments 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_float-
       ing_point<Floating>::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<Float-
       ing>::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 specifi-
       cations like the
	  Itanium ABI do not include the instantiation-dependent  portions  of
       non-type	template
	  parameters in	the mangling, meaning that specializations of two dis-
       tinct 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  con-
       sider them to be
	  the same entity.

Example
       // Run this code

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

	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  con-
       structible 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()
	{
	    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>{}; // 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		  2022.07.31		     std::enable_if(3)

Want to link to this manual page? Use this URL:
<https://man.freebsd.org/cgi/man.cgi?query=std::enable_if&sektion=3&manpath=FreeBSD+Ports+15.0>

home | help