From 46f8fa5e6fb66f3d9d7a8f7a8a9603aceeece8e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaakko=20J=C3=A4rvi?= Date: Wed, 2 Jul 2003 21:34:05 +0000 Subject: [PATCH] added [SVN r1400] --- doc/enable_if.html | 362 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 doc/enable_if.html diff --git a/doc/enable_if.html b/doc/enable_if.html new file mode 100644 index 0000000..1b927fb --- /dev/null +++ b/doc/enable_if.html @@ -0,0 +1,362 @@ + + +enable_if + + + + + + + + + + +
+
+ + +

+enable_if

+
+
+Copyright 2003 Jaakko Järvi, Jeremiah Willcock, Andrew Lumsdaine.
+
+ + +

1  Introduction

+ + +The enable_if template is a tool for selectively including +functions into the overload resolution set based on the properties of +the 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 template can also be +applied to enable class template specializations. Applications of +enable_if are discussed in length +in [1] and [2].
+
+ + +

1.1  Synopsis

+ + +
+namespace boost {
+  template <bool B, class T = void> struct enable_if;
+  template <bool B, class T = void> struct disable_if;
+  template <bool B, class T> struct enable_if_lazy;
+  template <bool B, class T> struct disable_if_lazy;
+}
+
+ + +

1.2  Background

+ + +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 was 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 template is a tool for controlled creation of the SFINAE +conditions.
+
+ + +

2  The enable_if template

+ + +The definition of enable_if is as follows: +
+
+template <bool B, class T = void>
+struct enable_if {
+  typedef T type;
+};
+
+template <class T>
+struct enable_if<false, T> {};
+
+
+An instantiation of the enable_if 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<B, T>::type is either a valid or an invalid type +expression, depending on the value of B. +When valid, enable_if<B, T>::type equals T. +The enable_if can thus be used for controlling when functions are considered for +overload resolution and when 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<boost::is_arithmetic<T>::value, T>::type foo(T t) { return t; }
+
+
+The disable_if template is provided as well, and has the +same functionality as enable_if except for the negated condition. The following +function is enabled for all non-arithmetic types. +
+
+template <class T>
+typename disable_if<boost::is_arithmetic<T>::value, T>::type foo(T t) { return t; }
+
+
+ + +

3  Using enable_if

+ + +The enable_if templates are defined in +boost/utility/enable_if.hpp, which is included by boost/utility.hpp.
+
+The enable_if template can be used either as the return type, or as an +extra argument. For example, the foo function below could also be written +as: +
+
+template <class T>
+typename T foo(T t, typename enable_if<boost::is_arithmetic<T> >::type* dummy = 0); 
+
+
Hence, an extra argument of type void* is added, but it is given +a default value to keep the argument hidden from the client code. +Note that the type parameter was not given to enable_if, the default +void gives the desired type.
+
+Whether to write the enabler as an argument or within the return type is +largely a matter of taste, but for certain functions, only one +alternative is possible: + + + +

3.1  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>::value>::type> { ... };
+
+template <class T>
+class A<T, typename enable_if<is_float<T>::value>::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 no type parameter is given to enable_if; the default (void) +is the correct value.
+
+ + +

3.2  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>::value, void>::type foo(T t) {}
+
+template <class T>
+typename enable_if<boost::is_arithmetic<T>::value, 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.
+
+ + +

3.3  Lazy enable_if

+ + +The standard is not entirely clear on how deep into nested template instantiations SFINAE applies, +and there is variation among compilers. For example: +
+
+template <class T, class U> class mult_traits;
+
+template <class T, class U>
+typename enable_if<is_multipliable<T, U>::value, 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 enable_if_lazy +and disable_if_lazy templates can be used in such +situations: +
+
+template<class T, class U>
+typename enable_if_lazy<is_multipliable<T, U>::value, mult_traits<T, U> >::type
+operator*(const T& t, const U& u) { ... }
+
+
The second argument of enable_if_lazy must be a class type +that defines a nested type named type whenever the first +parameter (the condition) is true.
+
+ + +

3.4  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> struct dummy { dummy(int) {} };
+
+template <class T>
+typename enable_if<boost::is_arithmetic<T>::value, T>::type foo(T t);
+
+template <class T>
+typename enable_if<!boost::is_arithmetic<T>::value, T>::type foo(T t);
+
+
Two workarounds can be applied: + + + +

4  Acknowledgements

+ +We are grateful to Howard Hinnant, Jason Shirk, Paul Mensonides and Richard +Smith whose findings have influenced the library.
+
+ + +

References

+
[1]
+Jaakko Järvi, Jeremiah Willcock, Howard Hinnant, and Andrew Lumsdaine. +Function overloading based on arbitrary properties of types. +C/C++ Users Journal, 21(6):25--32, June 2003.
+
+
[2]
+Jaakko Järvi, Jeremiah Willcock, and Andrew Lumsdaine. +Concept-controlled polymorphism. +In Proceedings of Generative Programming and Component + Engineering (GPCE'03), September 2003. +To appear.
+
+
[3]
+David Vandevoorde and Nicolai M. Josuttis. +C++ Templates: The Complete Guide. +Addison-Wesley, 2002.
+ + + + + +
+ +Contributed by:
+Jaakko Järvi, Jeremiah Willcock and Andrew Lumsdaine
+{jajarvi|jewillco|lums}@osl.iu.edu
+Indiana University
+Open Systems Lab + + + +
+
This document was translated from LATEX by +HEVEA. +
+ +