501 lines
16 KiB
Plaintext
501 lines
16 KiB
Plaintext
[/
|
|
/ 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]
|