More C++ Idioms/enable-if
From Wikibooks, the open-content textbooks collection
Contents |
[edit] Intent
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].
[edit] Also Known As
Substitution Failure Is Not An Error
[edit] Motivation
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 templates are tools for controlled creation of the SFINAE conditions.
[edit] Solution
template <bool, class T = void> struct enable_if {}; template <class T> struct enable_if<true, T> { typedef T type; };
[edit] Sample code
// following function is defined for all arithmetic types template <class T> typename enable_if_c<is_arithmetic<T>::value, T>::type foo(T t) { return t; } // the enable_if template can be used either as the return type, or as an extra argument. template <class T> T foo(T t, typename enable_if<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.
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:
- Operators have a fixed number of arguments, thus enable_if must be used in the return type.
- Constructors and destructors do not have a return type; an extra argument is the only option.
- There does not seem to be a way to specify an enabler for a conversion operator. Converting constructors, however, can have enablers as extra default arguments.
[edit] Known Uses
Boost library, C++0x STL, etc.
[edit] Related Idioms
[edit] References
- 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.
- Jaakko Järvi, 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.
- David Vandevoorde and Nicolai M. Josuttis. C++ Templates: The Complete Guide. Addison-Wesley, 2002.