for review

[SVN r1506]
This commit is contained in:
Jaakko Järvi
2003-09-05 23:07:24 +00:00
parent 60ae05a250
commit 6e56d76519

View File

@@ -7,7 +7,7 @@
<META name="GENERATOR" content="hevea 1.06">
</HEAD>
<BODY >
<!--HEVEA command line is: hevea -nosymb -noiso -pedantic -v enable_if_docs_for_boost.tex -->
<!--HEVEA command line is: hevea enable_if_docs_for_boost.tex -->
<!--HTMLHEAD-->
<!--ENDHTML-->
<!--PREFIX <ARG ></ARG>-->
@@ -20,18 +20,19 @@
<img border="0" src="c++boost.gif" align="center" width="277" height="86">enable_if</h1>
<BR>
<BR>
Copyright 2003 Jaakko J&auml;rvi, Jeremiah Willcock, Andrew Lumsdaine.<BR>
Copyright 2003 Jaakko J<EFBFBD>rvi, Jeremiah Willcock, Andrew Lumsdaine.<BR>
<BR>
<!--TOC section Introduction-->
<H2><A NAME="htoc1">1</A>&nbsp;&nbsp;Introduction</H2><!--SEC END -->
<A NAME="introduction"></A>
The <TT>enable_if</TT> 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
The <TT>enable_if</TT> 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 <TT>enable_if</TT> template can also be
defined by a traits class. The <TT>enable_if</TT> templates can also be
applied to enable class template specializations. Applications of
<TT>enable_if</TT> are discussed in length
in&nbsp;[<A HREF="#jarvi:03:cuj_arbitrary_overloading"><CITE>1</CITE></A>] and&nbsp;[<A HREF="#jarvi:03:c++typeclasses"><CITE>2</CITE></A>].<BR>
@@ -43,10 +44,15 @@ in&nbsp;[<A HREF="#jarvi:03:cuj_arbitrary_overloading"><CITE>1</CITE></A>] and&n
<A NAME="sec:synopsis"></A>
<PRE>
namespace boost {
template &lt;bool B, class T = void&gt; struct enable_if;
template &lt;bool B, class T = void&gt; struct disable_if;
template &lt;bool B, class T&gt; struct enable_if_lazy;
template &lt;bool B, class T&gt; struct disable_if_lazy;
template &lt;class Cond, class T = void&gt; struct enable_if;
template &lt;class Cond, class T = void&gt; struct disable_if;
template &lt;class Cond, class T&gt; struct lazy_enable_if;
template &lt;class Cond, class T&gt; struct lazy_disable_if;
template &lt;bool B, class T = void&gt; struct enable_if_c;
template &lt;bool B, class T = void&gt; struct disable_if_c;
template &lt;bool B, class T&gt; struct lazy_enable_if_c;
template &lt;bool B, class T&gt; struct lazy_disable_if_c;
}
</PRE>
<!--TOC subsection Background-->
@@ -85,50 +91,80 @@ where the return type is invalid. If this was an error, adding an unrelated func
Due to the SFINAE principle the above example is not, however, erroneous.
The latter definition of <TT>negate</TT> is simply removed from the overload resolution set.<BR>
<BR>
The <TT>enable_if</TT> template is a tool for controlled creation of the SFINAE
The <TT>enable_if</TT> templates are tools for controlled creation of the SFINAE
conditions.<BR>
<BR>
<!--TOC section The <TT>enable_if</TT> template-->
<!--TOC section The <TT>enable_if</TT> templates-->
<H2><A NAME="htoc4">2</A>&nbsp;&nbsp;The <TT>enable_if</TT> template</H2><!--SEC END -->
<H2><A NAME="htoc4">2</A>&nbsp;&nbsp;The <TT>enable_if</TT> templates</H2><!--SEC END -->
<A NAME="enable_if"></A>
The definition of <TT>enable_if</TT> is as follows:
The names of the <TT>enable_if</TT> templates have three parts: an optional <TT>lazy_</TT> tag,
either <TT>enable_if</TT> or <TT>disable_if</TT>, and an optional <TT>_c</TT> tag.
All eight combinations of these parts are supported.
The meaning of the <TT>lazy_</TT> tag is described in Section&nbsp;<A HREF="#sec:enable_if_lazy">3.3</A>.
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 <TT>bool</TT> value
(<TT>_c</TT> suffix), or a type containing a static <TT>bool</TT> constant named <TT>value</TT> (no suffix).
The latter version interoperates with Boost.MPL. <BR>
<BR>
The definitions of <TT>enable_if_c</TT> and <TT>enable_if</TT> are as follows (we use <TT>enable_if</TT> templates
unqualified but they are in the <TT>boost</TT> namespace).
<PRE>
template &lt;bool B, class T = void&gt;
struct enable_if {
struct enable_if_c {
typedef T type;
};
template &lt;class T&gt;
struct enable_if&lt;false, T&gt; {};
struct enable_if_c&lt;false, T&gt; {};
template &lt;class Cond, class T = void&gt;
struct enable_if : public enable_if_c&lt;Cond::value, T&gt; {};
</PRE>
An instantiation of the <TT>enable_if</TT> template with the parameter
An instantiation of the <TT>enable_if_c</TT> template with the parameter
<TT>B</TT> as <TT>true</TT> contains a member type <TT>type</TT>, defined
to be <TT>T</TT>. If <TT>B</TT> is
false, no such member is defined. Thus
<TT>enable_if&lt;B, T&gt;::type</TT> is either a valid or an invalid type
<TT>false</TT>, no such member is defined. Thus
<TT>enable_if_c&lt;B, T&gt;::type</TT> is either a valid or an invalid type
expression, depending on the value of <TT>B</TT>.
When valid, <TT>enable_if&lt;B, T&gt;::type</TT> equals <TT>T</TT>.
The <TT>enable_if</TT> can thus be used for controlling when functions are considered for
overload resolution and when not.
When valid, <TT>enable_if_c&lt;B, T&gt;::type</TT> equals <TT>T</TT>.
The <TT>enable_if_c</TT> 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 <A HREF="http://www.boost.org/libs/type_traits">Boost type_traits library</A>):
<PRE>
template &lt;class T&gt;
typename enable_if&lt;boost::is_arithmetic&lt;T&gt;::value, T&gt;::type foo(T t) { return t; }
typename enable_if_c&lt;boost::is_arithmetic&lt;T&gt;::value, T&gt;::type
foo(T t) { return t; }
</PRE>
The <TT>disable_if</TT> template is provided as well, and has the
same functionality as <TT>enable_if</TT> except for the negated condition. The following
The <TT>disable_if_c</TT> template is provided as well, and has the
same functionality as <TT>enable_if_c</TT> except for the negated condition. The following
function is enabled for all non-arithmetic types.
<PRE>
template &lt;class T&gt;
typename disable_if&lt;boost::is_arithmetic&lt;T&gt;::value, T&gt;::type foo(T t) { return t; }
typename disable_if_c&lt;boost::is_arithmetic&lt;T&gt;::value, T&gt;::type
bar(T t) { return t; }
</PRE>
For easier syntax in some cases and interoperation with Boost.MPL we provide versions of
the <TT>enable_if</TT> templates taking any type with a <TT>bool</TT> member constant named
<TT>value</TT> as the condition argument.
The MPL <TT>bool_</TT>, <TT>and_</TT>, <TT>or_</TT>, and <TT>not_</TT> 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 <TT>foo</TT> can be alternatively written as:
<PRE>
template &lt;class T&gt;
typename enable_if&lt;boost::is_arithmetic&lt;T&gt;, T&gt;::type
foo(T t) { return t; }
</PRE>
<!--TOC section Using <TT>enable_if</TT>-->
@@ -140,17 +176,17 @@ The <TT>enable_if</TT> templates are defined in
<TT>boost/utility/enable_if.hpp</TT>, which is included by <TT>boost/utility.hpp</TT>.<BR>
<BR>
The <TT>enable_if</TT> template can be used either as the return type, or as an
extra argument. For example, the <TT>foo</TT> function below could also be written
extra argument. For example, the <TT>foo</TT> function in the previous section could also be written
as:
<PRE>
template &lt;class T&gt;
typename T foo(T t, typename enable_if&lt;boost::is_arithmetic&lt;T&gt; &gt;::type* dummy = 0);
T foo(T t, typename enable_if&lt;boost::is_arithmetic&lt;T&gt; &gt;::type* dummy = 0);
</PRE>Hence, an extra argument of type <TT>void*</TT> 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 <TT>enable_if</TT>, the default
<TT>void</TT> gives the desired type.<BR>
</PRE>Hence, an extra parameter of type <TT>void*</TT> 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 <TT>enable_if</TT>, as the default
<TT>void</TT> gives the desired behavior.<BR>
<BR>
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
@@ -176,17 +212,17 @@ template &lt;class T, class Enable = void&gt;
class A { ... };
template &lt;class T&gt;
class A&lt;T, typename enable_if&lt;is_integral&lt;T&gt;::value&gt;::type&gt; { ... };
class A&lt;T, typename enable_if&lt;is_integral&lt;T&gt; &gt;::type&gt; { ... };
template &lt;class T&gt;
class A&lt;T, typename enable_if&lt;is_float&lt;T&gt;::value&gt;::type&gt; { ... };
class A&lt;T, typename enable_if&lt;is_float&lt;T&gt; &gt;::type&gt; { ... };
</PRE>Instantiating <TT>A</TT> 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 <TT>enable_if</TT>; the default (<TT>void</TT>)
Note that again, the second argument to <TT>enable_if</TT> is not needed; the default (<TT>void</TT>)
is the correct value.<BR>
<BR>
<!--TOC subsection Overlapping enabler conditions-->
@@ -203,10 +239,12 @@ lead to ambiguities. For example:
<PRE>
template &lt;class T&gt;
typename enable_if&lt;boost::is_integral&lt;T&gt;::value, void&gt;::type foo(T t) {}
typename enable_if&lt;boost::is_integral&lt;T&gt;, void&gt;::type
foo(T t) {}
template &lt;class T&gt;
typename enable_if&lt;boost::is_arithmetic&lt;T&gt;::value, void&gt;::type foo(T t) {}
typename enable_if&lt;boost::is_arithmetic&lt;T&gt;, void&gt;::type
foo(T t) {}
</PRE>
All integral types are also arithmetic. Therefore, say, for the call <TT>foo(1)</TT>,
@@ -230,7 +268,7 @@ and there is variation among compilers. For example:
template &lt;class T, class U&gt; class mult_traits;
template &lt;class T, class U&gt;
typename enable_if&lt;is_multipliable&lt;T, U&gt;::value, typename mult_traits&lt;T, U&gt;::type&gt;::type
typename enable_if&lt;is_multipliable&lt;T, U&gt;, typename mult_traits&lt;T, U&gt;::type&gt;::type
operator*(const T&amp; t, const U&amp; u) { ... }
</PRE>Assume the class template <TT>mult_traits</TT> is a traits class defining
@@ -243,16 +281,16 @@ Now, trying to invoke (some other overload) of <TT>operator*</TT> with, say, ope
for which <TT>is_multipliable&lt;C, D&gt;::value</TT> is <TT>false</TT>
and <TT>mult_traits&lt;C, D&gt;::type</TT> 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 <TT>enable_if_lazy</TT>
and <TT>disable_if_lazy</TT> templates can be used in such
the invalid type occurs as an argument to another template. The <TT>lazy_enable_if</TT>
and <TT>lazy_disable_if</TT> templates (and their <TT>_c</TT> versions) can be used in such
situations:
<PRE>
template&lt;class T, class U&gt;
typename enable_if_lazy&lt;is_multipliable&lt;T, U&gt;::value, mult_traits&lt;T, U&gt; &gt;::type
typename lazy_enable_if&lt;is_multipliable&lt;T, U&gt;, mult_traits&lt;T, U&gt; &gt;::type
operator*(const T&amp; t, const U&amp; u) { ... }
</PRE>The second argument of <TT>enable_if_lazy</TT> must be a class type
</PRE>The second argument of <TT>lazy_enable_if</TT> must be a class type
that defines a nested type named <TT>type</TT> whenever the first
parameter (the condition) is true.<BR>
<BR>
@@ -269,10 +307,12 @@ some compilers (e.g. GCC 3.2) diagnose the following two functions as ambiguous:
template &lt;class T&gt; struct dummy { dummy(int) {} };
template &lt;class T&gt;
typename enable_if&lt;boost::is_arithmetic&lt;T&gt;::value, T&gt;::type foo(T t);
typename enable_if&lt;boost::is_arithmetic&lt;T&gt;, T&gt;::type
foo(T t);
template &lt;class T&gt;
typename enable_if&lt;!boost::is_arithmetic&lt;T&gt;::value, T&gt;::type foo(T t);
typename disable_if&lt;boost::is_arithmetic&lt;T&gt;, T&gt;::type
foo(T t);
</PRE>Two workarounds can be applied:
<UL><LI>
@@ -283,10 +323,12 @@ it to hide the parameter from the caller. For example:
template &lt;class T&gt; struct dummy { dummy(int) {} };
template &lt;class T&gt;
typename enable_if&lt;boost::is_arithmetic&lt;T&gt;::value, T&gt;::type foo(T t, dummy&lt;0&gt; = 0);
typename enable_if&lt;boost::is_arithmetic&lt;T&gt;, T&gt;::type
foo(T t, dummy&lt;0&gt; = 0);
template &lt;class T&gt;
typename enable_if&lt;!boost::is_arithmetic&lt;T&gt;::value, T&gt;::type foo(T t, dummy&lt;1&gt; = 0);
typename disable_if&lt;boost::is_arithmetic&lt;T&gt;, T&gt;::type
foo(T t, dummy&lt;1&gt; = 0);
</PRE><BR>
<BR>
<LI>Define the functions in different namespaces and bring them into a common
@@ -295,21 +337,24 @@ namespace with <TT>using</TT> declarations:
namespace A {
template &lt;class T&gt;
typename enable_if&lt;boost::is_arithmetic&lt;T&gt;::value, T&gt;::type foo(T t);
typename enable_if&lt;boost::is_arithmetic&lt;T&gt;, T&gt;::type
foo(T t);
}
namespace B {
template &lt;class T&gt;
typename enable_if&lt;!boost::is_arithmetic&lt;T&gt;::value, T&gt;::type foo(T t);
typename disable_if&lt;boost::is_arithmetic&lt;T&gt;, T&gt;::type
foo(T t);
}
using A::foo;
using B::foo;
</PRE>
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 help for templated operators that
neither of the workarounds are of assistance for templated operators that
need to be defined as member functions (assignment and
subscript operators).
</UL>
@@ -317,19 +362,19 @@ subscript operators).
<H2><A NAME="htoc10">4</A>&nbsp;&nbsp;Acknowledgements</H2><!--SEC END -->
We are grateful to Howard Hinnant, Jason Shirk, Paul Mensonides and Richard
We are grateful to Howard Hinnant, Jason Shirk, Paul Mensonides, and Richard
Smith whose findings have influenced the library.<BR>
<BR>
<!--TOC section References-->
<H2>References</H2><!--SEC END -->
<DL COMPACT=compact><DT><A NAME="jarvi:03:cuj_arbitrary_overloading"><FONT COLOR=purple>[1]</FONT></A><DD>
Jaakko J&auml;rvi, Jeremiah Willcock, Howard Hinnant, and Andrew Lumsdaine.
Jaakko J<EFBFBD>rvi, Jeremiah Willcock, Howard Hinnant, and Andrew Lumsdaine.
Function overloading based on arbitrary properties of types.
<EM>C/C++ Users Journal</EM>, 21(6):25--32, June 2003.<BR>
<BR>
<DT><A NAME="jarvi:03:c++typeclasses"><FONT COLOR=purple>[2]</FONT></A><DD>
Jaakko J&auml;rvi, Jeremiah Willcock, and Andrew Lumsdaine.
Jaakko J<EFBFBD>rvi, Jeremiah Willcock, and Andrew Lumsdaine.
Concept-controlled polymorphism.
In <EM>Proceedings of Generative Programming and Component
Engineering (GPCE'03)</EM>, September 2003.
@@ -347,7 +392,7 @@ Addison-Wesley, 2002.</DL>
<hr></hr>
<B>Contributed by:</B> <BR>
Jaakko J&auml;rvi, Jeremiah Willcock and Andrew Lumsdaine<BR>
Jaakko J<EFBFBD>rvi, Jeremiah Willcock and Andrew Lumsdaine<BR>
<EM>{jajarvi|jewillco|lums}@osl.iu.edu</EM><BR>
Indiana University<BR>
Open Systems Lab