git-svn-id: https://cppannotations.svn.sourceforge.net/svnroot/cppannotations/trunk@512 f6dd340e-d3f9-0310-b409-bdd246841980
This commit is contained in:
Frank B. Brokken 2011-01-04 19:50:43 +00:00
parent e11d7fd37f
commit 0efd6fd8d5

View file

@ -1,22 +1,22 @@
Consider tt(string)'s member tt(insert). tt(String::insert) has several
overloaded implementations. It can be used to insert text (completely or
partially) provided in a tt(string) or in a tt(char const *) argument; to
partially) provided by a tt(string) or by a tt(char const *) argument; to
insert single characters a specified number of times; iterators can be used to
specify the range of characters to be inserted; etc., etc.. All in,
tt(string) offers five overloaded tt(insert) members.
tt(string) offers as many as five overloaded tt(insert) members.
Assume the existence of a class tt(Inserter) that is used to insert
information into all kinds of objects. Such a class could have a tt(string)
data member into which information can be inserted. tt(Inserter)'s interface
only partially has to copy tt(string)'s interface to realize this: only
tt(string::insert)'s interfaces must be duplicated. These duplicating
tt(string::insert)'s interfaces must be duplicated. The members duplicating
interfaces often contain one statement (calling the appropriate member
function of the object's data member) and are for this reason often
implemented in-line. These wrapper functions merely emi(forward) their
parameters to the appropriate member function of the object's data member.
implemented in-line. Such em(wrapper functions) merely emi(forward) their
parameters to the matching member functions of the object's data member.
Factory functions also frequently forward their parameters to the constructors
of objects that they return.
Another example is found in em(factory functions) that also frequently forward
their parameters to the constructors of objects that they return.
Before the C++0x standard the interfaces of overloaded functions needed to
be duplicated by the forwarding entity: tt(Inserter) needed to duplicate the
@ -40,14 +40,16 @@ Perfect forwarding is easily implemented:
em(rvalue reference parameter pack) (e.g., tt(Params && ... params));
itht(forward)(std::forward) is used to forward the forwarding function's
arguments to the nested function, keeping track of their types and
number. Before using tt(forward) the tthi(utility) header file must
have been included. The nested function is then called using this
stanza for its arguments: tt(std::forward<Params>(params) ...).
number. Before tt(forward) can be used the tthi(utility) header file
must have been included.
it() The nested function is called using this
stanza to specify its arguments: tt(std::forward<Params>(params) ...).
)
In the following example perfect forwarding is used to implement
tt(Inserter::insert). The tt(insert) function that's actually called now
depends on the types and number of arguments that are passed to
In the following example perfect forwarding is used to implement em(one) member
tt(Inserter::insert) that can be used to call any of the five overloaded
tt(string::insert) members. The tt(insert) function that's actually called now
simply depends on the types and number of arguments that are passed to
tt(Inserter::insert):
verb(
class Inserter
@ -69,7 +71,7 @@ tt(Inserter::insert):
};
)
A factory function returning a tt(Inserter) can also easily be implemented
A factory function returning an tt(Inserter) can also easily be implemented
using perfect forwarding. Rather than defining four overloaded factory
functions a single one now suffices. By providing the factory function with an
additional template type parameter specifying the class of the object to
@ -99,11 +101,11 @@ operator used by variadic functions.
Perfect forwarding was introduced in section ref(CONTRACTIONS): a template
function defining a tt(Type &&param), with tt(Type) being a template type
parameter will convert tt(Type &&) to tt(Tp &) if the function is called with
an argument of type tt(Tp &). Otherwise it will bind tt(Type) to tt(Tp),
parameter converts tt(Type &&) to tt(Tp &) if the function is called with
an argument of type tt(Tp &). Otherwise it binds tt(Type) to tt(Tp),
with tt(param) being defined as tt(Tp &&param). As a result an em(lvalue)
argument will bind to an lvalue-type (tt(Tp &)), while an em(rvalue) argument
will bind to an rvalue-type (tt(Tp &&)).
argument binds to an lvalue-type (tt(Tp &)), while an em(rvalue) argument
binds to an rvalue-type (tt(Tp &&)).
The function tt(std::forward) merely passes the argument (and its type) on to
the called function or object. Here is its simplified implementation:
@ -116,17 +118,18 @@ the called function or object. Here is its simplified implementation:
)
Since tt(T &&) turns into an lvalue reference when tt(forward) is called
with an lvalue (or lvalue reference) and remains an rvalue reference if
tt(forward) is called with an rvalue reference and since tt(forward) (like
tt(std::move)) anonymizes the variable passed as argument to tt(forward) the
argument value is forwarded while keeping its type
from the function's parameter to the called function's argument. This is
called em(perfect forwarding) as the called function will only be called if
the types of the arguments used when calling the `outer' function (e.g.,
tt(factory)) exactly match the types of the parameters of the called function
(e.g., tt(Class)'s constructor). Perfect forwarding therefore is a tool to
uphold type safety.
tt(forward) is called with an rvalue reference, and since tt(forward) (like
tt(std::move)) anonymizes the variable passed as argument to tt(forward), the
argument value is forwarded while passing its type from the function's
parameter to the called function's argument.
A cosmetic improvement to tt(forward) forces users of tt(forward) to
This is called em(perfect forwarding) as the nested function can only be
called if the types of the arguments that were used when calling the `outer'
function (e.g., tt(factory)) exactly match the types of the parameters of the
nested function (e.g., tt(Class)'s constructor). Perfect forwarding therefore
is a tool to realize type safety.
A cosmetic improvement to tt(forward) requires users of tt(forward) to
specify the type to use rather than to have the compiler deduct the type as a
result of the function template parameter type deduction's process. This is
realized by a small support struct template:
@ -144,12 +147,12 @@ implementation of tt(forward) thus becomes (cf. section ref(DISTINGUISH) for
an explanation of the use of tt(typename)):
verb(
typedef <type T>
T &&forward(typename identity<T>::type&& a)
T &&forward(typename identity<T>::type &&arg)
{
return a;
return arg;
}
)
Now tt(forward) must explicitly state the tt(a)'s plain type, as in:
Now tt(forward) must explicitly state tt(arg)'s type, as in:
verb(
std::forward<Params>(params)
)
@ -161,7 +164,7 @@ ref(CONTRACTIONS)) they can profitably be used to forward individual function
hi(forward: parameters)
parameters as well. Here is an example showing how an argument to a function
can be forwarded from a template to a function that is itself passed to the
template as a pointer to a (unspecified) function:
template as a pointer to an (unspecified) function:
verb(
template<typename Fun, typename ArgType>
void caller(Fun fun, ArgType &&arg)