|  | Home | Libraries | People | FAQ | More | 
        The enable_if family of templates
        is a set of tools to allow a function template or a class template specialization
        to include or exclude itself from a set of matching functions or specializations
        based on properties of its template arguments. For example, one can define
        function templates that are only enabled for, and thus only match, an arbitrary
        set of types defined by a traits class. The enable_if
        templates can also be applied to enable class template specializations. Applications
        of enable_if are discussed
        in length in [1] and [2].
      
namespace boost { template <class Cond, class T = void> struct enable_if; template <class Cond, class T = void> struct disable_if; template <class Cond, class T> struct lazy_enable_if; template <class Cond, class T> struct lazy_disable_if; template <bool B, class T = void> struct enable_if_c; template <bool B, class T = void> struct disable_if_c; template <bool B, class T> struct lazy_enable_if_c; template <bool B, class T> struct lazy_disable_if_c; }
Sensible operation of template function overloading in C++ relies on the SFINAE (substitution-failure-is-not-an-error) principle [3]: if an invalid argument or return type is formed during the instantiation of a function template, the instantiation is removed from the overload resolution set instead of causing a compilation error. The following example, taken from [1], demonstrates why this is important:
int negate(int i) { return -i; } template <class F> typename F::result_type negate(const F& f) { return -f(); }
          Suppose the compiler encounters the call negate(1).
          The first definition is obviously a better match, but the compiler must
          nevertheless consider (and instantiate the prototypes) of both definitions
          to find this out. Instantiating the latter definition with F as int
          would result in:
        
int::result_type negate(const int&);
          where the return type is invalid. If this were an error, adding an unrelated
          function template (that was never called) could break otherwise valid code.
          Due to the SFINAE principle the above example is not, however, erroneous.
          The latter definition of negate
          is simply removed from the overload resolution set.
        
          The enable_if templates
          are tools for controlled creation of the SFINAE conditions.
        
        The names of the enable_if
        templates have three parts: an optional lazy_
        tag, either enable_if or
        disable_if, and an optional
        _c tag. All eight combinations
        of these parts are supported. The meaning of the lazy_
        tag is described in the section below.
        The second part of the name indicates whether a true condition argument should
        enable or disable the current overload. The third part of the name indicates
        whether the condition argument is a bool
        value (_c suffix), or a type
        containing a static bool constant
        named value (no suffix).
        The latter version interoperates with Boost.MPL.
      
        The definitions of enable_if_c
        and enable_if are as follows
        (we use enable_if templates
        unqualified but they are in the boost
        namespace).
      
template <bool B, class T = void> struct enable_if_c { typedef T type; }; template <class T> struct enable_if_c<false, T> {}; template <class Cond, class T = void> struct enable_if : public enable_if_c<Cond::value, T> {};
        An instantiation of the enable_if_c
        template with the parameter B
        as true contains a member type
        type, defined to be T. If B
        is false, no such member is
        defined. Thus enable_if_c<B,
        T>::type is either a valid or an invalid type
        expression, depending on the value of B.
        When valid, enable_if_c<B, T>::type
        equals T. The enable_if_c template can thus be used for
        controlling when functions are considered for overload resolution and when
        they are not. For example, the following function is defined for all arithmetic
        types (according to the classification of the Boost type_traits
        library):
      
template <class T> typename enable_if_c<boost::is_arithmetic<T>::value, T>::type foo(T t) { return t; }
        The disable_if_c template
        is provided as well, and has the same functionality as enable_if_c
        except for the negated condition. The following function is enabled for all
        non-arithmetic types.
      
template <class T> typename disable_if_c<boost::is_arithmetic<T>::value, T>::type bar(T t) { return t; }
        For easier syntax in some cases and interoperation with Boost.MPL we provide
        versions of the enable_if
        templates taking any type with a bool
        member constant named value
        as the condition argument. The MPL bool_,
        and_, or_,
        and not_ templates are likely
        to be useful for creating such types. Also, the traits classes in the Boost.Type_traits
        library follow this convention. For example, the above example function
        foo can be alternatively
        written as:
      
template <class T> typename enable_if<boost::is_arithmetic<T>, T>::type foo(T t) { return t; }
        The enable_if templates are
        defined in boost/utility/enable_if.hpp, which is included by boost/utility.hpp.
      
        With respect to function templates, enable_if
        can be used in multiple different ways:
      
        In the previous section, the return type form of enable_if
        was shown. As an example of using the form of enable_if
        that works via an extra function parameter, the foo
        function in the previous section could also be written as:
      
template <class T> T foo(T t, typename enable_if<boost::is_arithmetic<T> >::type* dummy = 0);
        Hence, an extra parameter of type void* is added, but it is given a default value
        to keep the parameter hidden from client code. Note that the second template
        argument was not given to enable_if,
        as the default void gives the
        desired behavior.
      
Which way to write the enabler is largely a matter of taste, but for certain functions, only a subset of the options is possible:
enable_if
            must be used either in the return type or in an extra template parameter.
          
          In a compiler which supports C++0x default arguments for function template
          parameters, you can enable and disable function templates by adding an
          additional template parameter. This approach works in all situations where
          you would use either the return type form of enable_if
          or the function parameter form, including operators, constructors, variadic
          function templates, and even overloaded conversion operations.
        
As an example:
#include <boost/type_traits/is_arithmetic.hpp> #include <boost/type_traits/is_pointer.hpp> #include <boost/utility/enable_if.hpp> class test { public: // A constructor that works for any argument list of size 10 template< class... T, typename boost::enable_if_c< sizeof...( T ) == 10, int >::type = 0> test( T&&... ); // A conversion operation that can convert to any arithmetic type template< class T, typename boost::enable_if< boost::is_arithmetic< T >, int >::type = 0> operator T() const; // A conversion operation that can convert to any pointer type template< class T, typename boost::enable_if< boost::is_pointer< T >, int >::type = 0> operator T() const; }; int main() { // Works test test_( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ); // Fails as expected test fail_construction( 1, 2, 3, 4, 5 ); // Works by calling the conversion operator enabled for arithmetic types int arithmetic_object = test_; // Works by calling the conversion operator enabled for pointer types int* pointer_object = test_; // Fails as expected struct {} fail_conversion = test_; }
          Class template specializations can be enabled or disabled with enable_if. One extra template parameter
          needs to be added for the enabler expressions. This parameter has the default
          value void. For example:
        
template <class T, class Enable = void> class A { ... }; template <class T> class A<T, typename enable_if<is_integral<T> >::type> { ... }; template <class T> class A<T, typename enable_if<is_float<T> >::type> { ... };
          Instantiating A with any
          integral type matches the first specialization, whereas any floating point
          type matches the second one. All other types match the primary template.
          The condition can be any compile-time boolean expression that depends on
          the template arguments of the class. Note that again, the second argument
          to enable_if is not needed;
          the default (void) is the
          correct value.
        
          The enable_if_has_type
          template is usable this scenario but instead of using a type traits to
          enable or disable a specialization, it use a SFINAE context to check for
          the existence of a dependent type inside its parameter. For example, the
          following structure extracts a dependent value_type
          from T if and only if T::value_type
          exists.
        
template <class T, class Enable = void> class value_type_from { typedef T type; }; template <class T> class value_type_from<T, typename enable_if_has_type<typename T::value_type>::type> { typedef typename T::value_type type; };
Once the compiler has examined the enabling conditions and included the function into the overload resolution set, normal C++ overload resolution rules are used to select the best matching function. In particular, there is no ordering between enabling conditions. Function templates with enabling conditions that are not mutually exclusive can lead to ambiguities. For example:
template <class T> typename enable_if<boost::is_integral<T>, void>::type foo(T t) {} template <class T> typename enable_if<boost::is_arithmetic<T>, void>::type foo(T t) {}
          All integral types are also arithmetic. Therefore, say, for the call foo(1), both
          conditions are true and both functions are thus in the overload resolution
          set. They are both equally good matches and thus ambiguous. Of course,
          more than one enabling condition can be simultaneously true as long as
          other arguments disambiguate the functions.
        
          The above discussion applies to using enable_if
          in class template partial specializations as well.
        
In some cases it is necessary to avoid instantiating part of a function signature unless an enabling condition is true. For example:
template <class T, class U> class mult_traits; template <class T, class U> typename enable_if<is_multipliable<T, U>, typename mult_traits<T, U>::type>::type operator*(const T& t, const U& u) { ... }
          Assume the class template mult_traits
          is a traits class defining the resulting type of a multiplication operator.
          The is_multipliable traits
          class specifies for which types to enable the operator. Whenever is_multipliable<A, B>::value
          is true for some types A and B,
          then mult_traits<A, B>::type
          is defined.
        
          Now, trying to invoke (some other overload) of operator* with, say, operand types C and D
          for which is_multipliable<C, D>::value is false
          and mult_traits<C, D>::type
          is not defined is an error on some compilers. The SFINAE principle is not
          applied because the invalid type occurs as an argument to another template.
          The lazy_enable_if and
          lazy_disable_if templates
          (and their _c versions)
          can be used in such situations:
        
template<class T, class U> typename lazy_enable_if<is_multipliable<T, U>, mult_traits<T, U> >::type operator*(const T& t, const U& u) { ... }
          The second argument of lazy_enable_if
          must be a class type that defines a nested type named type
          whenever the first parameter (the condition) is true.
        
| ![[Note]](../../../../../doc/src/images/note.png) | Note | 
|---|---|
| 
            Referring to one member type or static constant in a traits class causes
            all of the members (type and static constant) of that specialization
            to be instantiated. Therefore, if your traits classes can sometimes contain
            invalid types, you should use two distinct templates for describing the
            conditions and the type mappings. In the above example,  | 
Some compilers flag functions as ambiguous if the only distinguishing factor is a different condition in an enabler (even though the functions could never be ambiguous). For example, some compilers (e.g. GCC 3.2) diagnose the following two functions as ambiguous:
template <class T> typename enable_if<boost::is_arithmetic<T>, T>::type foo(T t); template <class T> typename disable_if<boost::is_arithmetic<T>, T>::type foo(T t);
Two workarounds can be applied:
Use an extra dummy parameter which disambiguates the functions. Use a default value for it to hide the parameter from the caller. For example:
template <int> struct dummy { dummy(int) {} }; template <class T> typename enable_if<boost::is_arithmetic<T>, T>::type foo(T t, dummy<0> = 0); template <class T> typename disable_if<boost::is_arithmetic<T>, T>::type foo(T t, dummy<1> = 0);
              Define the functions in different namespaces and bring them into a
              common namespace with using
              declarations:
            
namespace A { template <class T> typename enable_if<boost::is_arithmetic<T>, T>::type foo(T t); } namespace B { template <class T> typename disable_if<boost::is_arithmetic<T>, T>::type foo(T t); } using A::foo; using B::foo;
Note that the second workaround above cannot be used for member templates. On the other hand, operators do not accept extra arguments, which makes the first workaround unusable. As the net effect, neither of the workarounds are of assistance for templated operators that need to be defined as member functions (assignment and subscript operators).
We are grateful to Howard Hinnant, Jason Shirk, Paul Mensonides, and Richard Smith whose findings have influenced the library.