123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- [/
- / Copyright (c) 2003, 2004 Jaakko Jarvi
- / Copyright (c) 2008 John Maddock
- / Copyright (c) 2011, 2013 Jeremiah Willcock
- / Copyright (c) 2014 Glen Fernandes
- /
- / Use, modification, and distribution is subject to the Boost Software
- / License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
- / http://www.boost.org/LICENSE_1_0.txt)
- /]
- [section:enable_if enable_if]
- [simplesect Authors]
- * Jaakko J\u00E4rvi
- * Jeremiah Willcock
- * Andrew Lumsdaine
- [endsimplesect]
- [section Introduction]
- 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
- [link REF1 \[1\]] and [link REF2 \[2\]].
- [section Header <boost/core/enable_if.hpp>]
- ``
- 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;
- }
- ``
- [endsect]
- [section Background]
- Sensible operation of template function overloading in C++
- relies on the /SFINAE/ (substitution-failure-is-not-an-error)
- principle [link REF3 \[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 [link REF1 \[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.
- [endsect]
- [endsect]
- [section The enable_if templates]
- 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 [link
- core.enable_if.using_enable_if.enable_if_lazy 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; }
- ``
- [endsect]
- [section:using_enable_if Using enable_if]
- 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:
- * As the return type of an instantiatied function
- * As an extra parameter of an instantiated function
- * As an extra template parameter (useful only in a compiler
- that supports C++0x default arguments for function template
- parameters, see [link
- core.enable_if.using_enable_if.enable_if_0x Enabling function
- templates in C++0x] for details.
- 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:
- * Many operators have a fixed number of arguments, thus
- `enable_if` must be used either in the return type or in an
- extra template parameter.
- * Functions that have a variadic parameter list must use either
- the return type form or an extra template parameter.
- * Constructors do not have a return type so you must use either
- an extra function parameter or an extra template parameter.
- * Constructors that have a variadic parameter list must an
- extra template parameter.
- * Conversion operators can only be written with an extra
- template parameter.
- [section:enable_if_0x Enabling function templates in C++0x]
- 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_;
- }
- ``
- [endsect]
- [section Enabling template class specializations]
- 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;
- };
- ``
- [endsect]
- [section Overlapping enabler conditions]
- 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.
- [endsect]
- [section:enable_if_lazy Lazy enable_if]
- 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 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,
- `is_multipliable<T, U>::value` defines when
- `mult_traits<T, U>::type` is valid.]
- [endsect]
- [section Compiler workarounds]
- 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).
- [endsect]
- [endsect]
- [section Acknowledgements]
- We are grateful to Howard Hinnant, Jason Shirk, Paul
- Mensonides, and Richard Smith whose findings have
- influenced the library.
- [endsect]
- [section References]
- * [#REF1] \[1\] Jaakko J\u00E4rvi, Jeremiah Willcock, Howard
- Hinnant, and Andrew Lumsdaine. Function overloading based on
- arbitrary properties of types. /C++ Users Journal/,
- 21(6):25--32, June 2003.
- * [#REF2] \[2\] Jaakko J\u00E4rvi, Jeremiah Willcock, and
- Andrew Lumsdaine. Concept-controlled polymorphism. In Frank
- Pfennig and Yannis Smaragdakis, editors, /Generative
- Programming and Component Engineering/, volume 2830 of
- /LNCS/, pages 228--244. Springer Verlag, September 2003.
- * [#REF3] \[3\] David Vandevoorde and Nicolai M. Josuttis.
- /C++ Templates: The Complete Guide/. Addison-Wesley, 2002.
- [endsect]
- [endsect]
|