mirror of
https://github.com/boostorg/core.git
synced 2025-05-11 21:23:54 +00:00
enable_if_type allow to perform SFINAE check on the existence of a dependent type. It has been used here and there in various boost library but it's useful enough to warrant an autonomous existence.
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_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_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]
|