From 0efd6fd8d5ab448cf9f162eeba9f066e74976e23 Mon Sep 17 00:00:00 2001 From: "Frank B. Brokken" Date: Tue, 4 Jan 2011 19:50:43 +0000 Subject: [PATCH] wip git-svn-id: https://cppannotations.svn.sourceforge.net/svnroot/cppannotations/trunk@512 f6dd340e-d3f9-0310-b409-bdd246841980 --- yo/classtemplates/perfect.yo | 67 +++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/yo/classtemplates/perfect.yo b/yo/classtemplates/perfect.yo index 755ceae6..adf4ae45 100644 --- a/yo/classtemplates/perfect.yo +++ b/yo/classtemplates/perfect.yo @@ -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) ...). + 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) ...). ) -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 &¶m), 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 &¶m). 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 - T &&forward(typename identity::type&& a) + T &&forward(typename identity::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) ) @@ -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 void caller(Fun fun, ArgType &&arg)