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 most oper-
       ator overloads),
	    *  as  a  return type (not applicable to constructors and destruc-
       tors),
	    * 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 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_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  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 <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 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()
	{
	    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)

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.1.quarterly>

home | help