- Overview
-
Table of Contents
- Special Member Functions: Constructors, Destructors, and the Assignment Operator
- Operator Overloading
- Memory Management
- Templates
- Namespaces
- Time and Date Library
- Streams
- Object-Oriented Programming and Design Principles
- The Standard Template Library (STL) and Generic Programming
- Exception Handling
- Runtime Type Information (RTTI)
- Signal Processing
- Creating Persistent Objects
- Bit Fields
- New Cast Operators
- Environment Variables
- Variadic Functions
- Pointers to Functions
- Function Objects
- Pointers to Members
- Lock Files
- Design Patterns
- Dynamic Linking
- Tips and Techniques
- Five Things You Need to Know About C++11 Unions
- A Tour of C99
- A Tour of C1X
-
C++0X: The New Face of Standard C++
- Reference Wrapper
- The Performance Technical Report
- auto for the People
- Ironing Templates' Syntactic Wrinkles
- Visual C++ Becomes ISO Compliant
- A Garbage Collector for C++
- C99 Core Features in C++0X
- The <code>shared_ptr</code> Class
- The shared_ptr Class, II
- Lambda Expressions and Closures, Part I
- Lambda Expressions and Closures, Part II
- Lambda Expressions and Closures, Part III
- The Type Traits Library, Part I
- The Type Traits Library, Part II
- The Type Traits Library, Part III
- finally Revisited
- The Any Library
- The nullptr Keyword Proposal
- Delegating Constructors
- The Explicit Conversion Operators Proposal
- Conditionally-Supported Behavior
- The weak ptr Class Template, Part I
- The weak ptr Class Template, Part II
- POD Types Revisited
- The rvalue Reference Proposal, Part I
- The rvalue Reference Proposal, Part II
- Proposal for New String Algorithms
- Concepts, Part I
- Concepts, Part II
- constexpr: Generalized Constant Expressions
- The <u>constexpr</u> Proposal: Constructors
- Strongly-Typed enum Types
- C++09: The Road Ahead
- C++09: Proposals by Statuses
- Changing Undefined Behavior to Diagnosable Errors
- New Character Types
- The __func__ Predeclared Identifier is Coming to C++
- Static Assertions
- The extern template Proposal
- Variadic Templates, Part I
- Variadic Templates, Part II
- Variadic Templates, Part III -- Critique
- Using unique_ptr, Part I
- Using unique_ptr, Part II
- Unrestricted Unions, Part I
- Unrestricted Unions, Part II
- Unrestricted Unions, Part III
- Types With No Linkage as Template Arguments
- New Initialization Syntax
- Initializer Lists and Sequence Constructors
- New Standard Library Algorithms
- Class Member Initializers
- Inheriting Constructors
- Introducing Attributes
- The Removal of Concepts From C++0x
- The Future of C++0x, Part I
- The Future of C++0X, Part II
- The Debate About Attributes, Part I
- The Debate About Attributes, Part II
- The Debate About Attributes, Part III
- The Debate About Attributes, Part IV
- Forward Declarations of Enum Types
- The SCARY Iterators Proposal, Part I
- The SCARY Iterators Proposal, Part II
- Heading for Deprecation: <tt>export</tt>, Exception Specification and <tt>register</tt>
- The Rejection of the Unified Function Syntax Proposal
- Rvalue References as Object Members
- FCD Approved
- The Debate on noexcept, Part I
- The Debate on noexcept, Part II
- The Debate on noexcept, Part III
- About-face -- [[Attributes]] to Be Replaced with Keywords
- Will Delegating Constructors Be Removed From C++0x?
- Rvalue References: Past, Present and Future, Part I
- Rvalue References: Past, Present and Future, Part II
- Rvalue References: Past, Present and Future, Part III
- A Move in the Right Direction, Part I
- A Move in the Right Direction, Part II
- New Keywords for Inheritance Control, Part I
- New Keywords for Inheritance Control, Part II
- FDIS Approved
- C++0x Concurrency
- The Reflecting Circle
- We Have Mail
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
The rvalue Reference Proposal, Part II
Last updated Jan 1, 2003.
This part discusses the rules of rvalue references and explains how rvalue references solve the argument forwarding problem.
The Forwarding Problem
Generic function adaptors such as std::bind1st, std::bind, and std::tr1::function transform one function signature into another by binding some of the parameters. Ideally, the parameters that were left unbound in the transformed function should behave identically to matching parameters of the original function. For example, a const lvalue reference:
const T&
should bind to both lvalue and rvalue arguments, whereas a non-const lvalue reference:
T&
should bind to a non-const lvalue, but refuse to bind to rvalues and const lvalues:
template <class T> void g(T& t); int n=1; int&rn=n; const int& crn=n; g(rn);//OK, lvalue g(5); // compilation error, can’t bind T& to rvalue g(crn); //compilation error, can’t modify a const object
The common solution to this problem is const-based overloading, whereby each parameter gets two overloads:
template <class T> void g(T& t); #1 template <class T> void g(const T& t); #2 g(rn);// calls #1 g(5); // ok, calls #2 g(crn); //ok, calls #2
However, as the number of parameter grows, the number of overloads required increases exponentially. If you have two parameters, you will need four overloads of the same binder. For three parameters you’ll need 2^3 overloads.
Rvalue references enable you to implement what is known as perfect forwarding without resorting to const-based overloading. The proposed rules say that if t is a non-const lvalue reference, then the reference collapsing rules (explained below) ensure that T&& is a non-const lvalue reference, and thus will not bind to rvalues. If t is not a reference (i.e. pass-by-value), then the parameter T&& will bind to rvalues:
template <class T> void h(A & a1, const A& a2)
template <class T> void forward(T&& t1, T&& t2) {h(t1, t2); }
int n=0;
forward(1,10); //compilation error
forward (n, 10); //OK
In the first forward() call, the first argument is an rvalue that is
bound to t1. When t1 is passed to h(), a compilation
error occurs because it’s illegal to bind a non-const lvalue A& to an rvalue. In contemporary C++, you’d have to define two overloads of h() for this to work:
void (A& t,...); void (const A& t,...);In the second forward() call, the argument n is a non-cost lvalue. It binds to t1 while retaining its non-const lvalue identity. When t1 is forwarded to h(), it binds to a1, which is also a non-const lvalue. The second argument 10 is an rvalue. It can only bind to const A&.
In conclusion, the use of rvalue references enables you to implement perfect forwarding with a single version of forward().
Reference Collapsing Rules
Reference collapsing rules define what happens when a reference to references is formed. In C++98, there is only one reference collapsing rule: T& & or a reference to a reference, collapses to T&:
void g(int & ri) {++ri;} // int& & -> int&
void f(int & ri) {g(ri);}
The rvalue proposal extends the reference collapsing rules to include rvalue
references as well:
- A& & -> A& //as in C++98
- A& && -> A&
- A&& & -> A&
- A&& && -> A&&
According to these rules, a forwarding function can replicate both the cv qualification and l/rvalueness of its argument so that the forwarded-to function sees precisely the same argument.
Rvalue Cast
The rvalue reference proposal introduces new versions of the C++ cast operators which convert an operand to an rvalue. For example, the static_cast<T&&>(t) expression returns an lvalue if t is an lvalue type. Otherwise, the result is an rvalue:
template <class T>
T&& move(T&& x)
{
return static_cast<T&&>(x); //converts x to rvalue
}
Similar changes are proposed for the rest of the cast operators.
Move Semantics and RVO
In part I, I mentioned the Return Value Optimization (RVO). In essence, the RVO elides unnecessary copies in an assignment expression that calls a function which returns a local automatic object by value:
A f()
{
return A();
}
A b=f();
A naïve C++ implementation would perform the following steps to
initialize b:
- create a temporary A inside f()
- copy construct that temporary on the caller’s stack
- destroy the temporary created inside f()
- copy-construct b from the said copy
- destroy the said copy
Modern C++ compilers optimize away these steps by rewriting f() as void f(A&). The result is constructed directly into the lvalue argument. Thus, instead of creating copies, the implementation modifies the state of a given object directly.
The rvalue proposal addresses this optimization. The authors propose that a function returning a non-cv-qualified object with automatic storage should cast the result implicitly to rvalue:
string
operator+(const string& x, const string& y)
{
string result;
result.reserve(x.size() + y.size());
result = x;
result += y;
return result; // as if return static_cast<string&&>(result);
}
The rationale behind this implicit cast is an automatic hierarchy of move semantics:
- Elide the move/copy, if possible
- Else if there is a move constructor, use it
- Else if there is a copy constructor, use it
- Else the program is ill formed
Summary
The rvalue references proposal is directly related to other proposals for supporting move semantics and solutions to the argument forwarding problem. At this stage, the proposal is still being reviewed and is subjected to alterations.
Speaking of alterations, I’m not particularly keen on the T&& notation for two reasons: it could mislead readers into thinking that T is a reference to a reference (who knows, C++21xx might even need this beast!), just as T** denotes a pointer to a pointer. Additionally, T&& requires more keystrokes, which can be quite painful when dealing with long parameter lists. T! or T% seem better options, requiring fewer keystrokes while eliminating the confusion with lvalue references.
