Welcome to MSDN Blogs Sign in | Join | Help

Rvalue References: C++0x Features in VC10, Part 2

Part 1 of this series covered lambdas, auto, and static_assert.

 

Today, I'm going to talk about rvalue references, which enable two different things: move semantics and perfect forwarding.  This post will be long, because I'm going to explain how rvalue references work in great detail.  They're initially very confusing because they distinguish lvalues from rvalues, which very few C++98/03 programmers are extensively familiar with.

 

Fear not, for using rvalue references is easy, much easier than it initially sounds.  Implementing either move semantics or perfect forwarding in your own code boils down to following simple patterns, which I will demonstrate.  And it's definitely worth learning how to use rvalue references, as move semantics can produce order of magnitude performance improvements, and perfect forwarding makes writing highly generic code very easy.

 

 

lvalues and rvalues in C++98/03

 

In order to understand rvalue references in C++0x, you must first understand lvalues and rvalues in C++98/03.

 

The terminology of "lvalues" and "rvalues" is confusing because their history is confusing.  (By the way, they're just pronounced as "L values" and "R values", although they're written as single words.)  These concepts originally came from C, and then were elaborated upon by C++.  To save time, I'll skip over their history, including why they're called "lvalues" and "rvalues", and I'll go directly to how they work in C++98/03.  (Okay, it's not a big secret: "L" stands for "left" and "R" stands for "right".  But the concepts have evolved since the names were chosen, and the names aren't very accurate anymore.  Instead of going through the whole history lesson, you can consider the names to be arbitrary like "up quark" and "down quark", and you won't lose anything.)

 

C++03 3.10/1 says: "Every expression is either an lvalue or an rvalue."  It's important to remember that lvalueness versus rvalueness is a property of expressions, not of objects.

 

Lvalues name objects that persist beyond a single expression.  For example, obj , *ptr , ptr[index] , and ++x are all lvalues.

 

Rvalues are temporaries that evaporate at the end of the full-expression in which they live ("at the semicolon").  For example, 1729 , x + y , std::string("meow") , and x++ are all rvalues.

 

Notice the difference between ++x and x++ .  If we have int x = 0; then the expression x is an lvalue, as it names a persistent object.  The expression ++x is also an lvalue.  It modifies and then names the persistent object.  However, the expression x++ is an rvalue.  It copies the original value of the persistent object, modifies the persistent object, and then returns the copy.  This copy is a temporary.  Both ++x and x++ increment x, but ++x returns the persistent object itself, while x++ returns a temporary copy.  That's why ++x is an lvalue, while x++ is an rvalue.  Lvalueness versus rvalueness doesn't care about what an expression does, it cares about what an expression names (something persistent or something temporary).

 

If you want to build up intuition for this, another way to determine whether an expression is an lvalue is to ask "can I take its address?".  If you can, it's an lvalue.  If you can't, it's an rvalue.  For example, &obj , &*ptr , &ptr[index] , and &++x are all valid (even though some of those expressions are silly), while &1729 , &(x + y) , &std::string("meow") , and &x++ are all invalid.  Why does this work?  The address-of operator requires that its "operand shall be an lvalue" (C++03 5.3.1/2).  Why does it require that?  Taking the address of a persistent object is fine, but taking the address of a temporary would be extremely dangerous, because temporaries evaporate quickly.

 

The preceding examples ignore operator overloading, which is convenient syntax for a function call.  "A function call is an lvalue if and only if the result type is a reference." (C++03 5.2.2/10)  Therefore, given vector<int> v(10, 1729); , v[0] is an lvalue because operator[]() returns int& (and &v[0] is valid and useful), while given string s("foo"); and string t("bar"); , s + t is an rvalue because operator+() returns string (and &(s + t) is invalid).

 

Both lvalues and rvalues can be either modifiable (non-const) or non-modifiable (const).  Here are examples:

 

string one("cute");

const string two("fluffy");

string three() { return "kittens"; }

const string four() { return "are an essential part of a healthy diet"; }

 

one;     // modifiable lvalue

two;     // const lvalue

three(); // modifiable rvalue

four();  // const rvalue

 

Type& binds to modifiable lvalues (and can be used to observe and mutate them).  It can't bind to const lvalues, as that would violate const correctness.  It can't bind to modifiable rvalues, as that would be extremely dangerous.  Accidentally modifying temporaries, only to have the temporaries evaporate along with your modifications, would lead to subtle and obnoxious bugs, so C++ rightly prohibits this.  (I should mention that VC has an evil extension that allows this, but if you compile with /W4 , it warns when the evil extension is activated.  Usually.)  And it can't bind to const rvalues, as that would be doubly bad.  (Careful readers should note that I'm not talking about template argument deduction here.)

 

const Type& binds to everything: modifiable lvalues, const lvalues, modifiable rvalues, and const rvalues (and can be used to observe them).

 

A reference is a name, so a reference bound to an rvalue is itself an lvalue (yes, L).  (As only a const reference can be bound to an rvalue, it will be a const lvalue.)  This is confusing, and will be an extremely big deal later, so I'll explain further.  Given the function void observe(const string& str) , inside observe()'s implementation, str is a const lvalue, and its address can be taken and used before observe() returns.  This is true even though observe() can be called with rvalues, such as three() or four() above.  observe("purr") can also be called, which constructs a temporary string and binds str to that temporary.  The return values of three() and four() don't have names, so they're rvalues, but within observe(), str is a name, so it's an lvalue.  As I said above, "lvalueness versus rvalueness is a property of expressions, not of objects".  Of course, because str can be bound to a temporary which will evaporate, its address shouldn't be stored anywhere where it could be used after observe() returns.

 

Have you ever bound an rvalue to a const reference and then taken its address?  Yes, you have!  This is what happens when you write a copy assignment operator, Foo& operator=(const Foo& other) , with a self-assignment check, if (this != &other) { copy stuff; } return *this; , and you copy assign from a temporary, like Foo make_foo(); Foo f; f = make_foo(); .

 

At this point, you might ask, "So what's the difference between modifiable rvalues and const rvalues?  I can't bind Type& to modifiable rvalues, and I can't assign things (etc.) to modifiable rvalues, so can I really modify them?"  This is a very good question!  In C++98/03, the answer is that there's a slight difference: non-const member functions can be called on modifiable rvalues.  C++ doesn't want you to accidentally modify temporaries, but directly calling a non-const member function on a modifiable rvalue is explicit, so it's allowed.  In C++0x, the answer changes dramatically, making move semantics possible.

 

Congratulations!  Now you have what I call "lvalue/rvalue vision", the ability to look at an expression and determine whether it's an lvalue or an rvalue.  Combined with your "const vision", you can precisely reason that given void mutate(string& ref) and the definitions above, mutate(one) is valid, while mutate(two), mutate(three()), mutate(four()), and mutate("purr") are invalid, and all of observe(one), observe(two), observe(three()), observe(four()), and observe("purr") are valid.  If you're a C++98/03 programmer, you already knew which of these calls were valid and which were invalid; your "gut feeling", if not your compiler, would have told you that mutate(three()) was bogus.  Your new lvalue/rvalue vision tells you precisely why (three() is an rvalue, and modifiable references can't be bound to rvalues).  Is that useful?  To language lawyers, yes, but not really to normal programmers.  After all, you've gotten this far without knowing all of this stuff about lvalues and rvalues.  But here's the catch: compared to C++98/03, C++0x has vastly more powerful lvalue/rvalue vision (in particular, the ability to look at an expression, determine whether it's a modifiable/const lvalue/rvalue, and do something about it).  In order to use C++0x effectively, you need lvalue/rvalue vision too.  And now you have it, so we can proceed!

 

 

the copying problem

 

C++98/03 combines insanely powerful abstraction with insanely efficient execution, but it has a problem: it's overly fond of copying.  Things with value semantics behave like ints, so copying a thing doesn't modify the source, and the resulting copies are independent.  Value semantics are great, except that they tend to lead to unnecessary copies of heavy objects like strings, vectors, and so forth.  ("Heavy" means "expensive to copy"; a million-element vector is heavy.)  The Return Value Optimization (RVO) and Named Return Value Optimization (NRVO), where copy constructors are elided in certain situations, help to alleviate this problem, but they don't remove all unnecessary copies.

 

The most unnecessary copies are those where the source is about to be destroyed.  Would you photocopy a sheet of paper and then immediately throw away the original, assuming that the original and the photocopy are identical?  That would be wasteful; you should keep the original and not bother with the photocopy.  Here's what I call "the killer example", derived from one of the Standardization Committee's examples (in N1377).  Suppose that you have a bunch of strings, like this:

 

string s0("my mother told me that");

string s1("cute");

string s2("fluffy");

string s3("kittens");

string s4("are an essential part of a healthy diet");

 

And that you concatenate them like this:

 

string dest = s0 + " " + s1 + " " + s2 + " " + s3 + " " + s4;

 

How efficient is this?  (We're not worrying about this specific example, which executes in microseconds; we're worrying about its generalization, which occurs throughout the entire language.)

 

Each call to operator+() returns a temporary string.  There are 8 calls to operator+() , so there are 8 temporary strings.  Each one, upon its construction, performs a dynamic memory allocation and copies all of the characters that have been concatenated so far, and later, upon its destruction, performs a dynamic memory deallocation.  (If you've heard of the Small String Optimization, which VC performs in order to avoid dynamic memory allocations and deallocations for short strings, it's defeated here by my carefully chosen and sufficiently long s0 , and even if it applied, it couldn't avoid the copying.  If you've heard of the Copy-On-Write "optimization", forget about it - it doesn't apply here, and it's a pessimization under multithreading, so Standard Library implementations don't do it anymore.)

 

In fact, because every concatenation copies all of the characters that have been concatenated so far, this has quadratic complexity in the number of concatenations.  Yuck!  This is extraordinarily wasteful, which is especially embarrassing for C++.  Why is this happening, and what can we do about it?

 

The problem is that operator+() , which takes two const string& or one const string& and one const char * (there are other overloads, which we aren't using here), can't tell whether it's being fed lvalues versus rvalues, so it always has to create and return a new temporary string .  Why do lvalues versus rvalues matter?

 

When evaluating s0 + " " , it's absolutely necessary to create a new temporary strings0 is an lvalue, naming a persistent object, so we can't modify it.  (Someone would notice!)  But when evaluating (s0 + " ") + s1 , we could simply append s1's contents onto our first temporary string, instead of creating a second temporary and throwing the first temporary away.  This is the key insight behind move semantics: because s0 + " " is an rvalue, an expression referring to a temporary object, no one else in the entire program can observe that temporary object.  If we could detect that expression as being a modifiable rvalue, we could then proceed to modify the temporary object arbitrarily, without anyone else noticing.  operator+() isn't "supposed to" modify its arguments, but if they're modifiable rvalues, who cares?  In this manner, each call to operator+() can append characters onto a single temporary string .  This completely eliminates the unnecessary dynamic memory management and unnecessary copying, leaving us with linear complexity.  Yay!

 

Technically speaking, in C++0x, each call to operator+() still returns a separate temporary string .  However, the second temporary string (from evaluating (s0 + " ") + s1 ) is constructed by stealing the memory owned by the first temporary string (from evaluating s0 + " " ) and then appending s1's contents onto that memory (which may trigger an ordinary geometric reallocation).  "Stealing" consists of pointer twiddling: the second temporary copies and then nulls out the first temporary's internal pointer.  When the first temporary is eventually destroyed ("at the semicolon"), its pointer is null, so its destructor does nothing.

 

In general, being able to detect modifiable rvalues allows you to engage in "resource pilfering".  If the objects referred to by modifiable rvalues own any resources (such as memory), you can steal their resources instead of copying them, since they're going to evaporate anyways.  Constructing from or assigning from modifiable rvalues by taking what they own is generically referred to as "moving", and moveable objects have "move semantics".

 

This is extremely useful in many places, such as vector reallocation.  When a vector needs more capacity (e.g. during push_back()) and undergoes reallocation, it needs to copy elements from its old memory block to its new memory block.  These copy constructor calls can be expensive.  (With a vector<string>, each string needs to be copied, involving dynamic memory allocation.)  But wait!  The elements in the old memory block are about to be destroyed.  So we can move elements over, instead of copying them.  In this case, the elements in the old memory block occupy persistent storage, and the expressions used to refer to them, such as old_ptr[index] , are lvalues.  During reallocation, we want to refer to the elements in the old memory block with modifiable rvalue expressions.  Pretending that they're modifiable rvalues will allow us to move them, eliminating copy constructor calls.  (Saying "I want to pretend that this lvalue is a modifiable rvalue" is equivalent to saying "I know that this is an lvalue, referring to a persistent object, but I don't care what happens to the lvalue after this.  I'm going to destroy it, or assign to it, or whatever.  So if you can steal resources from it, go ahead.")

 

C++0x's rvalue references enable move semantics by giving us the ability to detect modifiable rvalues and steal from them.  Rvalue references also allow us to activate move semantics at will by treating lvalues as modifiable rvalues.  Now, let's see how rvalue references work!

 

 

rvalue references: initialization

 

C++0x introduces a new kind of reference, the rvalue reference, with the syntax Type&& and const Type&& .  The current C++0x Working Draft, N2798 8.3.2/2, says: "A reference type that is declared using & is called an lvalue reference, and a reference type that is declared using && is called an rvalue reference. Lvalue references and rvalue references are distinct types. Except where explicitly noted, they are semantically equivalent and commonly referred to as references."  This means that your intuition for C++98/03 references (now known as lvalue references) translates over to rvalue references; all you have to learn are the differences between them.

 

(Note: I've settled on pronouncing Type& as "Type ref" and Type&& as "Type ref ref".  They're fully known as "lvalue reference to Type" and "rvalue reference to Type", respectively, just like how "const pointer to int" is written as int * const and can be pronounced as "int star const".)

 

What are the differences?  Compared to lvalue references, rvalue references behave differently during initialization and overload resolution.  They differ in what they are willing to bind to (i.e. initialization) and what prefers to bind to them (i.e. overload resolution).  Let's look at initialization first:

 

·         We've already seen how the modifiable lvalue reference, Type& , is willing to bind to modifiable lvalues, but not to anything else (const lvalues, modifiable rvalues, const rvalues).

 

·         We've already seen how the const lvalue reference, const Type& , is willing to bind to everything.

 

·         The modifiable rvalue reference, Type&& , is willing to bind to modifiable lvalues and modifiable rvalues, but not to const lvalues and const rvalues (which would violate const correctness).

 

·         The const rvalue reference, const Type&& , is willing to bind to everything.

 

These rules may sound arcane, but they're derived from two simple rules:

 

·         Obey const correctness by preventing modifiable references from binding to const things.

 

·         Avoid accidentally modifying temporaries by preventing modifiable lvalue references from binding to modifiable rvalues.

 

If you like reading compiler errors instead of reading English, here's a demonstration:

 

C:\Temp>type initialization.cpp

#include <string>

using namespace std;

 

string modifiable_rvalue() {

    return "cute";

}

 

const string const_rvalue() {

    return "fluffy";

}

 

int main() {

    string modifiable_lvalue("kittens");

    const string const_lvalue("hungry hungry zombies");

 

    string& a = modifiable_lvalue;          // Line 16

    string& b = const_lvalue;               // Line 17 - ERROR

    string& c = modifiable_rvalue();        // Line 18 - ERROR

    string& d = const_rvalue();             // Line 19 - ERROR

 

    const string& e = modifiable_lvalue;    // Line 21

    const string& f = const_lvalue;         // Line 22

    const string& g = modifiable_rvalue();  // Line 23

    const string& h = const_rvalue();       // Line 24

 

    string&& i = modifiable_lvalue;         // Line 26

    string&& j = const_lvalue;              // Line 27 - ERROR

    string&& k = modifiable_rvalue();       // Line 28

    string&& l = const_rvalue();            // Line 29 - ERROR

 

    const string&& m = modifiable_lvalue;   // Line 31

    const string&& n = const_lvalue;        // Line 32

    const string&& o = modifiable_rvalue(); // Line 33

    const string&& p = const_rvalue();      // Line 34

}

 

C:\Temp>cl /EHsc /nologo /W4 /WX initialization.cpp

initialization.cpp

initialization.cpp(17) : error C2440: 'initializing' : cannot convert from 'const std::string' to 'std::string &'

        Conversion loses qualifiers

initialization.cpp(18) : warning C4239: nonstandard extension used : 'initializing' : conversion from 'std::string' to 'std::string &'

        A non-const reference may only be bound to an lvalue

initialization.cpp(19) : error C2440: 'initializing' : cannot convert from 'const std::string' to 'std::string &'

        Conversion loses qualifiers

initialization.cpp(27) : error C2440: 'initializing' : cannot convert from 'const std::string' to 'std::string &&'

        Conversion loses qualifiers

initialization.cpp(29) : error C2440: 'initializing' : cannot convert from 'const std::string' to 'std::string &&'

        Conversion loses qualifiers

 

It's okay for modifiable rvalue references to bind to modifiable rvalues; the whole point is that they can be used to modify temporaries.

 

Although lvalue references and rvalue references behave similarly during initialization (only lines 18 and 28 above differ), they increasingly diverge during overload resolution.

 

 

rvalue references: overload resolution

 

You're already familiar with how functions can be overloaded on modifiable and const lvalue reference parameters.  In C++0x, functions can also be overloaded on modifiable and const rvalue reference parameters.  Given all four overloads of a unary function, you shouldn't be surprised to discover that each expression prefers to bind to its corresponding reference:

 

C:\Temp>type four_overloads.cpp

#include <iostream>

#include <ostream>

#include <string>

using namespace std;

 

void meow(string& s) {

    cout << "meow(string&): " << s << endl;

}

 

void meow(const string& s) {

    cout << "meow(const string&): " << s << endl;

}

 

void meow(string&& s) {

    cout << "meow(string&&): " << s << endl;

}

 

void meow(const string&& s) {

    cout << "meow(const string&&): " << s << endl;

}

 

string strange() {

    return "strange()";

}

 

const string charm() {

    return "charm()";

}

 

int main() {

    string up("up");

    const string down("down");

 

    meow(up);

    meow(down);

    meow(strange());

    meow(charm());

}

 

C:\Temp>cl /EHsc /nologo /W4 four_overloads.cpp

four_overloads.cpp

 

C:\Temp>four_overloads

meow(string&): up

meow(const string&): down

meow(string&&): strange()

meow(const string&&): charm()

 

In practice, overloading on Type& , const Type& , Type&& , and const Type&& is not very useful.  A far more interesting overload set is const Type& and Type&& :

 

C:\Temp>type two_overloads.cpp

#include <iostream>

#include <ostream>

#include <string>

using namespace std;

 

void purr(const string& s) {

    cout << "purr(const string&): " << s << endl;

}

 

void purr(string&& s) {

    cout << "purr(string&&): " << s << endl;

}

 

string strange() {

    return "strange()";

}

 

const string charm() {

    return "charm()";

}

 

int main() {

    string up("up");

    const string down("down");

 

    purr(up);

    purr(down);

    purr(strange());

    purr(charm());

}

 

C:\Temp>cl /EHsc /nologo /W4 two_overloads.cpp

two_overloads.cpp

 

C:\Temp>two_overloads

purr(const string&): up

purr(const string&): down

purr(string&&): strange()

purr(const string&): charm()

 

How does this work?  Here are the rules:

 

·         The initialization rules have veto power.

 

·         Lvalues strongly prefer binding to lvalue references, and rvalues strongly prefer binding to rvalue references.

 

·         Modifiable expressions weakly prefer binding to modifiable references.

 

(By "veto", I mean that candidate functions which would involve forbidden bindings of expressions to references are immediately deemed to be "non-viable" and are removed from further consideration.)  Let's walk through the process of applying the rules.

 

·         For purr(up) , the initialization rules veto neither purr(const string&) nor purr(string&&)up is an lvalue, so it strongly prefers binding to the lvalue reference purr(const string&)up is modifiable, so it weakly prefers binding to the modifiable reference purr(string&&) .  The strongly preferred purr(const string&) wins.

 

·         For purr(down) , the initialization rules veto purr(string&&) due to const correctness, so purr(const string&) wins by default.

 

·         For purr(strange()) , the initialization rules veto neither purr(const string&) nor purr(string&&)strange() is an rvalue, so it strongly prefers binding to the rvalue reference purr(string&&)strange() is modifiable, so it weakly prefers binding to the modifiable reference purr(string&&) .  The doubly preferred purr(string&&) wins.

 

·         For purr(charm()) , the initialization rules veto purr(string&&) due to const correctness, so purr(const string&) wins by default.

 

The important thing to notice is that when you overload on const Type& and Type&& , modifiable rvalues bind to Type&& , while everything else binds to const Type& .  Therefore, this is the overload set for move semantics.

 

Important note: functions returning by value should return Type (like strange()) instead of const Type (like charm()).  The latter buys you virtually nothing (forbidding non-const member function calls) and prevents the move semantics optimization.

 

 

move semantics: the pattern

 

Here's a simple class, remote_integer, that stores a pointer to a dynamically allocated int .  (This is "remote ownership".)  Its default constructor, unary constructor, copy constructor, copy assignment operator, and destructor should all look very familiar to you.  I've additionally given it a move constructor and move assignment operator.  They're guarded by #ifdef MOVABLE so that I can demonstrate what happens with and without them; real code won't do this.

 

C:\Temp>type remote.cpp

#include <stddef.h>

#include <iostream>

#include <ostream>

using namespace std;

 

class remote_integer {

public:

    remote_integer() {

        cout << "Default constructor." << endl;

 

        m_p = NULL;

    }

 

    explicit remote_integer(const int n) {

        cout << "Unary constructor." << endl;

 

        m_p = new int(n);

    }

 

    remote_integer(const remote_integer& other) {

        cout << "Copy constructor." << endl;

 

        if (other.m_p) {

            m_p = new int(*other.m_p);

        } else {

            m_p = NULL;

        }

    }

 

#ifdef MOVABLE

    remote_integer(remote_integer&& other) {

        cout << "MOVE CONSTRUCTOR." << endl;

 

        m_p = other.m_p;

        other.m_p = NULL;

    }

#endif // #ifdef MOVABLE

 

    remote_integer& operator=(const remote_integer& other) {

        cout << "Copy assignment operator." << endl;

 

        if (this != &other) {

            delete m_p;

 

            if (other.m_p) {

                m_p = new int(*other.m_p);

            } else {

                m_p = NULL;

            }

        }

 

        return *this;

    }

 

#ifdef MOVABLE

    remote_integer& operator=(remote_integer&& other) {

        cout << "MOVE ASSIGNMENT OPERATOR." << endl;

 

        if (this != &other) {

            delete m_p;

 

            m_p = other.m_p;

            other.m_p = NULL;

        }

 

        return *this;

    }

#endif // #ifdef MOVABLE

 

    ~remote_integer() {

        cout << "Destructor." << endl;

 

        delete m_p;

    }

 

    int get() const {

        return m_p ? *m_p : 0;

    }

 

private:

    int * m_p;

};

 

remote_integer square(const remote_integer& r) {

    const int i = r.get();

 

    return remote_integer(i * i);

}

 

int main() {

    remote_integer a(8);

 

    cout << a.get() << endl;

 

    remote_integer b(10);

 

    cout << b.get() << endl;

 

    b = square(a);

 

    cout << b.get() << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 remote.cpp

remote.cpp

 

C:\Temp>remote

Unary constructor.

8

Unary constructor.

10

Unary constructor.

Copy assignment operator.

Destructor.

64

Destructor.

Destructor.

 

C:\Temp>cl /EHsc /nologo /W4 /DMOVABLE remote.cpp

remote.cpp

 

C:\Temp>remote

Unary constructor.

8

Unary constructor.

10

Unary constructor.

MOVE ASSIGNMENT OPERATOR.

Destructor.

64

Destructor.

Destructor.

 

There are several things to notice here.

 

·         The copy and move constructors are overloaded, and the copy and move assignment operators are overloaded.  We've already seen what happens to functions overloaded on const Type& and Type&& .  This is what allows b = square(a); to automatically select the move assignment operator when it's available.

 

·         Instead of dynamically allocating memory, the move constructor and move assignment operator simply steal it from other .  When stealing, we copy other's pointer and then null it out.  When other is destroyed, its destructor will do nothing.

 

·         Both the copy and move assignment operators need self-assignment checks.  It's well-known why copy assignment operators need self-assignment checks.  This is because plain old data types like ints can be assigned to themselves harmlessly (e.g. with x = x;), so user-defined data types should also be harmlessly self-assignable.  Self-assignment virtually never happens in handwritten code, but it can easily happen inside algorithms like std::sort() .  In C++0x, algorithms like std::sort() can move elements around instead of copying them.  The same potential for self-assignment exists here.

 

At this point, you may be wondering how this interacts with automatically generated ("implicitly declared" in Standardese) constructors and assignment operators.

 

·         Move constructors and move assignment operators are never implicitly declared.

 

·         The implicit declaration of a default constructor is inhibited by any user-declared constructors, including copy constructors and move constructors.

 

·         The implicit declaration of a copy constructor is inhibited by a user-declared copy constructor, but not a user-declared move constructor.

 

·         The implicit declaration of a copy assignment operator is inhibited by a user-declared copy assignment operator, but not a user-declared move assignment operator.

 

Basically, the automatic generation rules don't interact with move semantics, except that declaring a move constructor, like declaring any constructor, inhibits the implicitly declared default constructor.

 

 

move semantics: moving from lvalues

 

Now, what if you like to write your copy constructors in terms of your copy assignment operators?  You might attempt to write your move constructors in terms of your move assignment operators.  This is possible, but you have to be careful.  Here's the wrong way to do it:

 

C:\Temp>type unified_wrong.cpp

#include <stddef.h>

#include <iostream>

#include <ostream>

using namespace std;

 

class remote_integer {

public:

    remote_integer() {

        cout << "Default constructor." << endl;

 

        m_p = NULL;

    }

 

    explicit remote_integer(const int n) {

        cout << "Unary constructor." << endl;

 

        m_p = new int(n);

    }

 

    remote_integer(const remote_integer& other) {

        cout << "Copy constructor." << endl;

 

        m_p = NULL;

        *this = other;

    }

 

#ifdef MOVABLE

    remote_integer(remote_integer&& other) {

        cout << "MOVE CONSTRUCTOR." << endl;

 

        m_p = NULL;

        *this = other; // WRONG

    }

#endif // #ifdef MOVABLE

 

    remote_integer& operator=(const remote_integer& other) {

        cout << "Copy assignment operator." << endl;

 

        if (this != &other) {

            delete m_p;

 

            if (other.m_p) {

                m_p = new int(*other.m_p);

            } else {

                m_p = NULL;

            }

        }

 

        return *this;

    }

 

#ifdef MOVABLE

    remote_integer& operator=(remote_integer&& other) {

        cout << "MOVE ASSIGNMENT OPERATOR." << endl;

 

        if (this != &other) {

            delete m_p;

 

            m_p = other.m_p;

            other.m_p = NULL;

        }

 

        return *this;

    }

#endif // #ifdef MOVABLE

 

    ~remote_integer() {

        cout << "Destructor." << endl;

 

        delete m_p;

    }

 

    int get() const {

        return m_p ? *m_p : 0;

    }

 

private:

    int * m_p;

};

 

remote_integer frumple(const int n) {

    if (n == 1729) {

        return remote_integer(1729);

    }

 

    remote_integer ret(n * n);

 

    return ret;

}

 

int main() {

    remote_integer x = frumple(5);

 

    cout << x.get() << endl;

 

    remote_integer y = frumple(1729);

 

    cout << y.get() << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 /O2 unified_wrong.cpp

unified_wrong.cpp

 

C:\Temp>unified_wrong

Unary constructor.

Copy constructor.

Copy assignment operator.

Destructor.

25

Unary constructor.

1729

Destructor.

Destructor.

 

C:\Temp>cl /EHsc /nologo /W4 /O2 /DMOVABLE unified_wrong.cpp

unified_wrong.cpp

 

C:\Temp>unified_wrong

Unary constructor.

MOVE CONSTRUCTOR.

Copy assignment operator.

Destructor.

25

Unary constructor.

1729

Destructor.

Destructor.

 

(The compiler is performing the RVO here, but not the NRVO.  As I mentioned earlier, some copy constructor calls are elided by the RVO and NRVO, but the compiler isn't always able to apply them.  Move constructors optimize the remaining cases.)

 

The line marked WRONG inside the move constructor is calling the copy assignment operator!  This compiles and runs, but it defeats the purpose of the move constructor.

 

What happened?  Remember from C++98/03 that named lvalue references are lvalues (if you say int& r = *p; then r is an lvalue) and unnamed lvalue references are also lvalues (given vector<int> v(10, 1729), calling v[0] returns int& , an unnamed lvalue reference whose address you can take).  Rvalue references behave differently:

 

·         Named rvalue references are lvalues.

 

·         Unnamed rvalue references are rvalues.

 

A named rvalue reference is an lvalue because it can be repeatedly mentioned, with multiple operations performed on it.  If instead it were an rvalue, then the first operation performed could steal from it, affecting subsequent operations.  Stealing is supposed to be unobservable, so this is forbidden.  On the other hand, unnamed rvalue references can't be repeatedly mentioned, so they can preserve their rvalueness.

 

If you're really intent on implementing your move constructors in terms of your move assignment operators, you'll need the ability to move from lvalues by treating them as rvalues.  This is powered by std::move() from C++0x <utility>, which will be in VC10 (in fact, it's already in my development build), but because it's not in the VC10 CTP, I'll show you how to write it from scratch:

 

C:\Temp>type unified_right.cpp

#include <stddef.h>

#include <iostream>

#include <ostream>

using namespace std;

 

template <typename T> struct RemoveReference {

     typedef T type;

};

 

template <typename T> struct RemoveReference<T&> {

     typedef T type;

};

 

template <typename T> struct RemoveReference<T&&> {

     typedef T type;

};

 

template <typename T> typename RemoveReference<T>::type&& Move(T&& t) {

    return t;

}

 

class remote_integer {

public:

    remote_integer() {

        cout << "Default constructor." << endl;

 

        m_p = NULL;

    }

 

    explicit remote_integer(const int n) {

        cout << "Unary constructor." << endl;

 

        m_p = new int(n);

    }

 

    remote_integer(const remote_integer& other) {

        cout << "Copy constructor." << endl;

 

        m_p = NULL;

        *this = other;

    }

 

#ifdef MOVABLE

    remote_integer(remote_integer&& other) {

        cout << "MOVE CONSTRUCTOR." << endl;

 

        m_p = NULL;

        *this = Move(other); // RIGHT

    }

#endif // #ifdef MOVABLE

 

    remote_integer& operator=(const remote_integer& other) {

        cout << "Copy assignment operator." << endl;

 

        if (this != &other) {

            delete m_p;

 

            if (other.m_p) {

                m_p = new int(*other.m_p);

            } else {

                m_p = NULL;

            }

        }

 

        return *this;

    }

 

#ifdef MOVABLE

    remote_integer& operator=(remote_integer&& other) {

        cout << "MOVE ASSIGNMENT OPERATOR." << endl;

 

        if (this != &other) {

            delete m_p;

 

            m_p = other.m_p;

            other.m_p = NULL;

        }

 

        return *this;

    }

#endif // #ifdef MOVABLE

 

    ~remote_integer() {

        cout << "Destructor." << endl;

 

        delete m_p;

    }

 

    int get() const {

        return m_p ? *m_p : 0;

    }

 

private:

    int * m_p;

};

 

remote_integer frumple(const int n) {

    if (n == 1729) {

        return remote_integer(1729);

    }

 

    remote_integer ret(n * n);

 

    return ret;

}

 

int main() {

    remote_integer x = frumple(5);

 

    cout << x.get() << endl;

 

    remote_integer y = frumple(1729);

 

    cout << y.get() << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 /O2 /DMOVABLE unified_right.cpp

unified_right.cpp

 

C:\Temp>unified_right

Unary constructor.

MOVE CONSTRUCTOR.

MOVE ASSIGNMENT OPERATOR.

Destructor.

25

Unary constructor.

1729

Destructor.

Destructor.

 

(I'll refer to std::move() and my Move() interchangeably, since they're implemented identically.)  How does std::move() work?  For the moment, I'm going to say that "magic is involved".  (There's a full explanation below; it's not complicated, but it involves template argument deduction and reference collapsing, which we'll encounter while looking at perfect forwarding.)  I can skip over the magic with concrete examples.  Given an lvalue of type string, like up from the overload resolution examples above, std::move(up) calls string&& std::move(string&) .  This returns an unnamed rvalue reference, which is an rvalue.  Given an rvalue of type string, like strange() from above, std::move(strange()) calls string&& std::move(string&&) .  Again, this returns an unnamed rvalue reference, which is an rvalue.

 

std::move() is useful in other places than implementing move constructors in terms of move assignment operators.  Whenever you reach a point where you can say "I have an lvalue here, and its value is going to cease to matter" (e.g. because it's going to be destroyed or assigned to), you can write std::move(your lvalue expression) in order to activate move semantics.

 

 

move semantics: movable members

 

C++0x's Standard classes (e.g. vector, string, regex) have move constructors and move assignment operators, and we've seen how to implement them in our own classes that manually manage resources (e.g. remote_integer).  But what about classes containing movable data members (e.g. vector, string, regex, remote_integer)?  The compiler won't automatically generate move constructors and move assignment operators for us.  So, we'll need to write them ourselves.  Fortunately, with std::move() , this is extremely easy:

 

C:\Temp>type point.cpp

#include <stddef.h>

#include <iostream>

#include <ostream>

using namespace std;

 

template <typename T> struct RemoveReference {

     typedef T type;

};

 

template <typename T> struct RemoveReference<T&> {

     typedef T type;

};

 

template <typename T> struct RemoveReference<T&&> {

     typedef T type;

};

 

template <typename T> typename RemoveReference<T>::type&& Move(T&& t) {

    return t;

}

 

class remote_integer {

public:

    remote_integer() {

        cout << "Default constructor." << endl;

 

        m_p = NULL;

    }

 

    explicit remote_integer(const int n) {

        cout << "Unary constructor." << endl;

 

        m_p = new int(n);

    }

 

    remote_integer(const remote_integer& other) {

        cout << "Copy constructor." << endl;

 

        if (other.m_p) {

            m_p = new int(*other.m_p);

        } else {

            m_p = NULL;

        }

    }

 

    remote_integer(remote_integer&& other) {

        cout << "MOVE CONSTRUCTOR." << endl;

 

        m_p = other.m_p;

        other.m_p = NULL;

    }

 

    remote_integer& operator=(const remote_integer& other) {

        cout << "Copy assignment operator." << endl;

 

        if (this != &other) {

            delete m_p;

 

            if (other.m_p) {

                m_p = new int(*other.m_p);

            } else {

                m_p = NULL;

            }

        }

 

        return *this;

    }

 

    remote_integer& operator=(remote_integer&& other) {

        cout << "MOVE ASSIGNMENT OPERATOR." << endl;

 

        if (this != &other) {

            delete m_p;

 

            m_p = other.m_p;

            other.m_p = NULL;

        }

 

        return *this;

    }

 

    ~remote_integer() {

        cout << "Destructor." << endl;

 

        delete m_p;

    }

 

    int get() const {

        return m_p ? *m_p : 0;

    }

 

private:

    int * m_p;

};

 

class remote_point {

public:

    remote_point(const int x_arg, const int y_arg)

        : m_x(x_arg), m_y(y_arg) { }

 

    remote_point(remote_point&& other)

        : m_x(Move(other.m_x)),

          m_y(Move(other.m_y)) { }

 

    remote_point& operator=(remote_point&& other) {

        m_x = Move(other.m_x);

        m_y = Move(other.m_y);

        return *this;

    }

 

    int x() const { return m_x.get(); }

    int y() const { return m_y.get(); }

 

private:

    remote_integer m_x;

    remote_integer m_y;

};

 

remote_point five_by_five() {

    return remote_point(5, 5);

}

 

remote_point taxicab(const int n) {

    if (n == 0) {

        return remote_point(1, 1728);

    }

 

    remote_point ret(729, 1000);

 

    return ret;

}

 

int main() {

    remote_point p = taxicab(43112609);

 

    cout << "(" << p.x() << ", " << p.y() << ")" << endl;

 

    p = five_by_five();

 

    cout << "(" << p.x() << ", " << p.y() << ")" << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 /O2 point.cpp

point.cpp

 

C:\Temp>point

Unary constructor.

Unary constructor.

MOVE CONSTRUCTOR.

MOVE CONSTRUCTOR.

Destructor.

Destructor.

(729, 1000)

Unary constructor.

Unary constructor.

MOVE ASSIGNMENT OPERATOR.

MOVE ASSIGNMENT OPERATOR.

Destructor.

Destructor.

(5, 5)

Destructor.

Destructor.

 

As you can see, memberwise moves are trivial to write.  Note that remote_point's move assignment operator doesn't need to check for self-assignment because remote_integer already does.  Also note that remote_point's implicitly declared copy constructor, copy assignment operator, and destructor do the right things.

 

By now, you should be exhaustively familiar with move semantics.  (But hopefully not exhausted!)  As a test of your newly acquired incredible powers, solving the original example of the copying problem with operator+() (applied to remote_integer instead of string) is an exercise left to the reader. 

 

A final reminder: whenever possible, you should implement move constructors and move assignment operators for your copyable classes, because the compiler won't do it for you.  Not only will ordinary use of those classes automatically pick up move semantics, but STL containers and algorithms will also take advantage of move semantics, replacing expensive copies with cheap moves.

 

 

the forwarding problem

 

C++98/03's rules for lvalues, rvalues, references, and templates seemed perfectly fine until programmers tried to write highly generic code.  Suppose you're writing a completely generic function outer() whose purpose in life is to take an arbitrary number of arbitrary arguments and pass them ("forward" them) to an arbitrary function inner() .  There are many good examples of this.  The factory function make_shared<T>(args) forwards args to T's constructor and returns shared_ptr<T> .  (This can store the T object and its reference count control block in a single dynamic memory allocation, achieving efficiency equal to that of intrusive reference counting.)  Wrappers like function<Ret (Args)> forward arguments to their stored functors, and so forth.  In this post, our only interest is outer() forwarding arguments to inner() .  Determining outer()'s return type is a separate matter (some cases are easy, e.g. make_shared<T>(args) always returns shared_ptr<T>, but the C++0x feature decltype is required to solve the fully general case).

 

The zero-argument case solves itself, so what about the one-argument case?  Let's try to write outer() :

 

template <typename T> void outer(T& t) {

    inner(t);

}

 

The problem with this outer() is that it can't be called with modifiable rvalues.  If inner() takes const int& , then inner(5) will compile.  But outer(5) won't compile; T will be deduced to be int, and int& won't bind to 5 .

 

Okay, so let's try this:

 

template <typename T> void outer(const T& t) {

    inner(t);

}

 

If inner() takes int& , then this will violate const correctness, so it won't compile.

 

Now, you can overload outer() on both T& and const T& , which actually works.  Then you can call outer() exactly as if it were inner() .

 

Unfortunately, this doesn't scale to the multi-argument case.  That would involve overloading on T1& and const T1& , T2& and const T2& , etc. for every argument, producing an exponential number of overloads.  (VC9 SP1's tr1::bind() is desperate enough to do this for the first 5 arguments, involving 63 overloads.  Otherwise, it would be hard to explain to users why they couldn't call bound functors with rvalues like 1729 without going through this whole explanation.  Stamping out these overloads requires disgusting preprocessor machinery that you really don't want to know about.)

 

The forwarding problem is severe and fundamentally unsolvable in C++98/03 (without resorting to that disgusting preprocessor machinery that significantly slows down compilation and makes code virtually unreadable).  However, rvalue references solve the forwarding problem in an elegant manner.

 

(I explained the initialization and overload resolution rules before demonstrating the move semantics pattern, but now I'm going to demonstrate the perfect forwarding pattern before explaining the template argument deduction and reference collapsing rules.  It'll make more sense this way.)

 

 

perfect forwarding: the pattern

 

Perfect forwarding allows you to write a single function template that takes N arbitrary arguments and forwards them, transparently, to an arbitrary function.  Their modifiable/const lvalue/rvalue nature will be preserved, permitting outer() to be used exactly like inner() , and cooperating with move semantics as a bonus.  (C++0x variadic templates solve the "arbitrary number" part; we'll be considering any given N here.)  This is easy, although it looks like magic at first:

 

C:\Temp>type perfect.cpp

#include <iostream>

#include <ostream>

using namespace std;

 

template <typename T> struct Identity {

    typedef T type;

};

 

template <typename T> T&& Forward(typename Identity<T>::type&& t) {

    return t;

}

 

void inner(int&, int&) {

    cout << "inner(int&, int&)" << endl;

}

 

void inner(int&, const int&) {

    cout << "inner(int&, const int&)" << endl;

}

 

void inner(const int&, int&) {

    cout << "inner(const int&, int&)" << endl;

}

 

void inner(const int&, const int&) {

    cout << "inner(const int&, const int&)" << endl;

}

 

template <typename T1, typename T2> void outer(T1&& t1, T2&& t2) {

    inner(Forward<T1>(t1), Forward<T2>(t2));

}

 

int main() {

    int a = 1;

    const int b = 2;

 

    cout << "Directly calling inner()." << endl;

 

    inner(a, a);

    inner(b, b);

    inner(3, 3);

 

    inner(a, b);

    inner(b, a);

 

    inner(a, 3);

    inner(3, a);

 

    inner(b, 3);

    inner(3, b);

 

    cout << endl << "Calling outer()." << endl;

 

    outer(a, a);

    outer(b, b);

    outer(3, 3);

 

    outer(a, b);

    outer(b, a);

 

    outer(a, 3);

    outer(3, a);

 

    outer(b, 3);

    outer(3, b);

}

 

C:\Temp>cl /EHsc /nologo /W4 perfect.cpp

perfect.cpp

 

C:\Temp>perfect

Directly calling inner().

inner(int&, int&)

inner(const int&, const int&)

inner(const int&, const int&)

inner(int&, const int&)

inner(const int&, int&)

inner(int&, const int&)

inner(const int&, int&)

inner(const int&, const int&)

inner(const int&, const int&)

 

Calling outer().

inner(int&, int&)

inner(const int&, const int&)

inner(const int&, const int&)

inner(int&, const int&)

inner(const int&, int&)

inner(int&, const int&)

inner(const int&, int&)

inner(const int&, const int&)

inner(const int&, const int&)

 

Two lines!  Perfect forwarding takes two lines!  That's neat.

 

This demonstrates that outer() forwards t1 and t2 to inner() transparently; inner() observes lvalueness/rvalueness and constness as if it had been directly called.

 

Like std::move(), std::identity and std::forward() are defined by C++0x <utility> (and will be in VC10, but aren't in the CTP); I'm demonstrating how they're implemented.  (Again, I'll refer to std::identity and my Identity, and std::forward() and my Forward(), interchangeably, since they're implemented identically.)

 

Now, let's figure out how the magic works.  It's powered by template argument deduction and reference collapsing.

 

 

rvalue references: template argument deduction and reference collapsing

 

Rvalue references and templates interact in a special way.  This demonstrates what happens:

 

C:\Temp>type collapse.cpp

#include <iostream>

#include <ostream>

#include <string>

using namespace std;

 

template <typename T> struct Name;

 

template <> struct Name<string> {

    static const char * get() {

        return "string";

    }

};

 

template <> struct Name<const string> {

    static const char * get() {

        return "const string";

    }

};

 

template <> struct Name<string&> {

    static const char * get() {

        return "string&";

    }

};

 

template <> struct Name<const string&> {

    static const char * get() {

        return "const string&";

    }

};

 

template <> struct Name<string&&> {

    static const char * get() {

        return "string&&";

    }

};

 

template <> struct Name<const string&&> {

    static const char * get() {

        return "const string&&";

    }

};

 

template <typename T> void quark(T&& t) {

    cout << "t: " << t << endl;

    cout << "T: " << Name<T>::get() << endl;

    cout << "T&&: " << Name<T&&>::get() << endl;

    cout << endl;

}

 

string strange() {

    return "strange()";

}

 

const string charm() {

    return "charm()";

}

 

int main() {

    string up("up");

    const string down("down");

 

    quark(up);

    quark(down);

    quark(strange());

    quark(charm());

}

 

C:\Temp>cl /EHsc /nologo /W4 collapse.cpp

collapse.cpp

 

C:\Temp>collapse

t: up

T: string&

T&&: string&

 

t: down

T: const string&

T&&: const string&

 

t: strange()

T: string

T&&: string&&

 

t: charm()

T: const string

T&&: const string&&

 

The explicit specializations of Name allow us to print out types.

 

When we call quark(up), template argument deduction is performed.  quark() is a function template with a template parameter T, but we haven't provided an explicit template argument (which would look like quark<X>(up)).  Instead, a template argument can be deduced by comparing the function parameter type T&& with the function argument type (an lvalue of type string).

 

C++0x transforms both the function parameter type and the function argument type before matching them together.

 

First, it transforms the function argument type.  A special rule is activated (N2798 14.8.2.1 [temp.deduct.call]/3): when the function parameter type is of the form T&& where T is a template parameter, and the function argument is an lvalue of type A, the type A& is used for template argument deduction.  (This special rule doesn't apply to function parameter types of the form T& or const T& , which behave as they did in C++98/03, nor does it apply to const T&& .)  In the case of quark(up), this means that we transform string into string& .

 

Then, it transforms the function parameter type.  Both C++98/03 and C++0x discard references (C++0x discards both lvalue references and rvalue references).  In all four cases, this means that we transform T&& into T .

 

Therefore, we deduce T to be the transformed function argument type.  That's why quark(up) prints "T: string&" and quark(down) prints "T: const string&"; up and down are lvalues, so they activate the special rule.  strange() and charm() are rvalues, so they use the ordinary rules, which is why quark(strange()) prints "T: string" and quark(charm()) prints "T: const string".

 

Substitution is performed after template argument deduction.  Each occurrence of the template parameter T is replaced by the deduced template argument.  In quark(strange()), T is string, so T&& is string&& .  Similarly, in quark(charm()), T is const string, so T&& is const string&& .  However, quark(up) and quark(down) activate another special rule.

 

In quark(up) , T is string& .  Substituting that into T&& produces string& && .  References to references are collapsed in C++0x, and the reference collapsing rule is that "lvalue references are infectious".  X& & , X& && , and X&& & collapse to X& .  Only X&& && collapses to X&& .  So, string& && collapses to string& .  In templates, things that look like rvalue references aren't necessarily so.  quark(up) instantiates quark<string&>(). Within this instantiation, T&& is string& .  We've observed this with Name<T&&>::get().  Similarly, when quark(down) instantiates quark<const string&>(), T&& is const string& .  In C++98/03, you're probably used to constness hiding inside template parameters (a function template taking T& can be called with a const Foo object; what looks like T& will be const Foo&).  In C++0x, lvalueness can hide inside template parameters.

 

Okay, so what do these two special rules buy us?  Within quark(), the type T&& has the same lvalueness/rvalueness and constness as quark()'s function argument.  This is how rvalue references preserve lvalueness/rvalueness and constness for perfect forwarding.

 

 

perfect forwarding: how std::forward() and std::identity work

 

Let's look at outer() again:

 

template <typename T1, typename T2> void outer(T1&& t1, T2&& t2) {

    inner(Forward<T1>(t1), Forward<T2>(t2));

}

 

Now we understand why outer() takes T1&& and T2&& .  These types preserve information about outer()'s arguments.  But why does it call Forward<T1>() and Forward<T2>() ?  Recall that both named lvalue references and named rvalue references are lvalues.  If outer() called inner(t1, t2), then inner() would always observe t1 and t2 as being lvalues, breaking perfect forwarding.

 

Fortunately, unnamed lvalue references are lvalues, and unnamed rvalue references are rvalues.  So, in order to forward t1 and t2 to inner(), we need to pass them through helper functions that preserve their types but remove their names.  This is what std::forward() does:

 

template <typename T> struct Identity {

    typedef T type;

};

 

template <typename T> T&& Forward(typename Identity<T>::type&& t) {

    return t;

}

 

When we call Forward<T1>(t1), Identity doesn't modify T1 (we'll soon see what it's doing).  So Forward<T1>() takes T1&& and returns T1&& .  This leaves t1's type unchanged (whatever it is; we've seen that it can be string& , const string& , string&& , or const string&&) but removes its name.  inner() will observe Forward<T1>(t1) as having the same type, lvalueness/rvalueness, and constness as whatever was passed as outer()'s first argument.  That's how perfect forwarding works!

 

You might wonder what would happen if you accidentally wrote Forward<T1&&>(t1) .  (This mistake is slightly tempting, since outer() takes T1&& t1 .)  Fortunately, nothing bad would happen.  Forward<T1&&>() would take and return T1&& && .  This collapses to T1&& .  Therefore, Forward<T1>(t1) and Forward<T1&&>(t1) are identical, but the former is shorter, so it should be preferred.

 

What is Identity doing?  Why wouldn't this work:

 

template <typename T> T&& Forward(T&& t) { // BROKEN

    return t;

}

 

If Forward() were written like that, it could be called without explicit template arguments.  Template argument deduction would kick in, and we've seen what happens to T&& - it becomes an lvalue reference when its function argument is an lvalue.  And the whole problem that we're trying to solve with Forward() is that within outer(), the named t1 and t2 are lvalues even when their types T1&& and T2&& are rvalue references.  With the BROKEN implementation, Forward<T1>(t1) would still work, but Forward(t1) would compile (so tempting!) and do the wrong thing, acting just like plain t1 .  That would be a continual source of pain, so Identity is used to disable template argument deduction.  Experienced template programmers should be familiar with this, as it works identically in C++98/03 and C++0x: the double colon in typename Identity<T>::type is a sheet of lead, and template argument deduction can't see through to the left of it.  (Explaining that is another story.)

 

 

move semantics: how std::move() works

 

Now that we've learned the special rules for template argument deduction and reference collapsing, let's look at std::move() again:

 

template <typename T> struct RemoveReference {

     typedef T type;

};

 

template <typename T> struct RemoveReference<T&> {

     typedef T type;

};

 

template <typename T> struct RemoveReference<T&&> {

     typedef T type;

};

 

template <typename T> typename RemoveReference<T>::type&& Move(T&& t) {

    return t;

}

 

The RemoveReference machinery exactly replicates std::remove_reference from C++0x <type_traits>.  For example, RemoveReference<string>::type , RemoveReference<string&>::type , and RemoveReference<string&&>::type are all string .

 

Similarly, the Move() machinery exactly replicates std::move() from C++0x <utility>.

 

·         When called with an lvalue of type string, T is deduced to be string& , so Move() takes string& (after collapsing) and returns string&& (after RemoveReference).

 

·         When called with an lvalue of type const string, T is deduced to be const string& , so Move() takes const string& (after collapsing) and returns const string&& (after RemoveReference).

 

·         When called with an rvalue of type string, T is deduced to be string , so Move() takes string&& and returns string&& .

 

·         When called with an rvalue of type const string, T is deduced to be const string , so Move() takes const string&& and returns const string&& .

 

This is how Move() preserves the type and constness of its argument, but converts lvalues into rvalues.

 

 

the past

 

To learn more about rvalue references, you can read their proposal papers.  Note that things have changed since these proposals were written; rvalue references have been integrated into the C++0x Working Paper, where they're continuing to receive wording tweaks.  Some parts of the proposals are incorrect, outdated, or discussing alternatives that weren't adopted.  Still, they're very informative.

 

N1377, N1385, and N1690 are the main proposal papers.  N2118 contains the final revision of the proposed wording before it was integrated into the Working Paper.  N1784, N1821, N2377, and N2439 trace the evolution of "Extending Move Semantics To *this", which has been integrated into C++0x but not yet implemented in VC10.

 

 

the future

 

N2812 "A Safety Problem With Rvalue References (and what to do about it)" proposes a change to the initialization rules, forbidding rvalue references from binding to lvalues.  It turns out that this won't affect the move semantics and perfect forwarding patterns, so it won't invalidate the new techniques that you just learned (but it will change how std::move() and std::forward() are implemented).

 

Stephan T. Lavavej

Visual C++ Libraries Developer

Published Tuesday, February 03, 2009 9:27 AM by vcblog

Comments

# Rvalue References: C++0x Features in VC10, Part 2 &raquo; Click &amp; Solve

Tuesday, February 03, 2009 2:13 PM by Mike Diack

# re: Rvalue References: C++0x Features in VC10, Part 2

And people say C++ is complex ;)

I wonder how many people will use this additional capability.... I won't be one of them, but hopefully it'll be useful for others.

Mike

Tuesday, February 03, 2009 2:16 PM by JK

# re: Rvalue References: C++0x Features in VC10, Part 2

I'm very excited about rvalue references.  I can think of a number of places they will prove useful.

Tuesday, February 03, 2009 2:29 PM by Tom

# re: Rvalue References: C++0x Features in VC10, Part 2

I'm a longtime C++ developer (20 years), and I recently started working in C#, and in Java before that.

I am beginning to develop the opinion that C++ is being crushed under its own weight.  By that I mean that the language and libraries have become so complicated that the learning curve to achieve proficiency is way too high.  That causes a large number of C++ programmers to not be able to achieve proficiency in the language, as well as other developers to outright avoid the language because of its complexity.

And at the end of the day, while the complexity in C++ may be an interesting intellectual challenge, and it may be necessary in order to support evolution of the language in its current form, one has to really whether this approach makes any sense considering the "big picture."  For example, what problems are significantly easier to solve in C++ compared to C#?  C# has a learning curve just a fraction of C++, C# is much safer than C++, it is also much more productive than C++.

The only potential benefit I see to C++ is its cross-platform abilities and its potential performance benefits relative to C#.  But since most of us are actually programming Windows apps in C++ on pretty fast hardware, is this really any benefit at all?

Personally, I am not optimistic about the future of C++, although it is a language that I really enjoy(ed) and invested a lot of time learning.  I just feel that any new project that I personally approach now or in the future, I would find other languages much more compelling.

Tuesday, February 03, 2009 3:21 PM by Stephan T. Lavavej [MSFT]

# re: Rvalue References: C++0x Features in VC10, Part 2

[Mike Diack]

> I wonder how many people will use this additional

> capability.... I won't be one of them, but

> hopefully it'll be useful for others.

Virtually everyone should be writing move constructors and move assignment operators.

Tuesday, February 03, 2009 4:28 PM by Stewart

# re: Rvalue References: C++0x Features in VC10, Part 2

> I wonder how many people will use this additional

> capability.... I won't be one of them, but

> hopefully it'll be useful for others.

This is my favourite C++0x feature so far - anyone writing moderately large C++ code using the STL should see big perf wins from this and it is so easy to use. You don't need to understand this article to use it, but this is a great article all the same. Thanks.

Tuesday, February 03, 2009 4:58 PM by Stuart Dootson

# re: Rvalue References: C++0x Features in VC10, Part 2

@ Mike

> I wonder how many people will use this additional

> capability.... I won't be one of them, but

> hopefully it'll be useful for others.

You will be using it indirectly, if nothing else - the standard C++ library objects will, I'm sure, have move semantics implemented for them. The potential performance benefits make that highly likely.

Wednesday, February 04, 2009 1:47 AM by BF

# re: Rvalue References: C++0x Features in VC10, Part 2

I understand that MS is just implementing the standards, and not really creating them, so they should be commended for that. I just question the direction the standard is taking.

I concur with comments above about C++'s future. The template programming model in C++ has gotten way out of hand. I thought it was out of hand 10 yrs ago, but now, it's waaay  waaay out of hand. It's a perfect example of Write Only Code. You can write it, but no one else can understand what you did, including perhaps yourself just a few minutes later. And god forbid you misplace a & or two. You could spend days and weeks, and longer trying to figure out what's wrong. This is very poor language design, IMHO. Sure, it's intellectually stimulating exercise, but I question the productivity of it, indeed, the sanity of it. Surely there is a better way.

I've been a long time fan of C++, having invested over 20 yrs in it. But when STL came out in the early nineties or whenever it was, I took one look at it, and thought, "You've got to be kidding me." And now, it's even worse. I shake my head and wonder about the fact that the C++ designers have still yet to tackle C++'s main problem, and that is its antiquated header file mechanism. Really, that's so 1970's. Couldn't we move on from that? Having to including thousands upon thousands of lines of template gibberish just to access a simple collection class, for example, is adding insult to injury. Why not spend just a little time on moving the language to a more modern mechanism for referencing other modules, such as in C# and Java.

I've dabbled with C#, but to me it's fatal flaw is lack of deterministic destruction. So I stick with C++, but I only use templates in very simplistic ways and sparingly. I avoid STL like the plague. I'd rather have to hand code and duplicate what would otherwise be templated classes and algorithms, and be able to read and understand what was written, than try to navigate through the C++ template thickets.

To me, when I scan through an STL header file, it might as well be a sheet of lead. Very little understanding can get through one of those files.

I long for a language that combines the best of C++ (runtime efficiency with moderately high level abstraction coupled with low-level access), with the smoothness and slickness of C# (better GUI support, IDE support, slick modern features, but without the frustrating non-deterministic destructors design that was imho a big big mistake.) C++/CLI is almost that, but even MS seems to treat that language as for limited interop use.

Wednesday, February 04, 2009 8:52 AM by LaurentD

# re: Rvalue References: C++0x Features in VC10, Part 2

Commenters above do seem to get the point of C++. Quoting from the article: "[C++] combines insanely powerful abstraction with insanely efficient execution".

Today's C++ has a very specific purpose. Its complexity is primarily a consequence of its underlying philosophy: "don't pay for what you don't use", and of its backward-compatibility with C. If that's of no interest to you then you should *not* use C++. There are other cross-platforms languages around, that are easier to learn and arguably more productive, like Java or Python.

You must realize that C++ design is primarily a consequence of its insane requirements, and for those requirements, it's still the best language around.

Wednesday, February 04, 2009 10:43 AM by Mike Diack

# re: Rvalue References: C++0x Features in VC10, Part 2

I don't want this to descend into a flame war.

As I tried to make clear earlier. I'm not disputing the usefulness of this for some people, but I suspect an equally large number of people won't (knowingly anyway) make any use of it. That's not to belittle its usefulness.

However surely no one would deny that such syntax is becoming increasingly arcane - using templates anyway is moderately difficult...

But I'm glad it's useful for some people.

Mike

Wednesday, February 04, 2009 1:36 PM by

# re: Rvalue References: C++0x Features in VC10, Part 2

Thanks for the article. As for the development of C++ I think this is the path to take. The design goals for C++ should be to create a language that is as fast as C but have most/all of the features of other languages (through the language itself or libraries). If 'fast enough' is fast enough you have plenty of languages to choose from but in many cases there is no such thing as 'fast enough' and then C++ is your only option if you want object orientation, templates etc etc.

Wednesday, February 04, 2009 10:05 PM by R.J

# re: Rvalue References: C++0x Features in VC10, Part 2

Thanks for the nice article.

Alongside with the concepts, it is my favorite C++0x feature and I'm looking forward to using it.

Wednesday, February 04, 2009 10:48 PM by CMC

# re: Rvalue References: C++0x Features in VC10, Part 2

<williamshatnervoice>

eyes.....bleeding

brain....mush

must.....sleep

</williamshatnervoice>

Thursday, February 05, 2009 6:43 AM by Andreas

# re: Rvalue References: C++0x Features in VC10, Part 2

@Mike: > As I tried to make clear earlier. I'm not disputing the usefulness of this for some people

I think you're missing the point, this *is* useful to all people using STL, e.g. std::vector<std::string>, which is IMHO the only reasonable way to program in C++ (the using STL way). You don't have to understand this, you don't have to explicitly use this. Use STL, enjoy the performance boost and be happy!

Seriously, this makes it possible to use std::vector<std::vector<std::string>> without bringing your machine to its knees.

@Tom: > And at the end of the day, while the complexity in C++ may be an interesting intellectual challenge

It surprises me that a long-time C++-programmer doesn't realize that this is mainly a feature for use in library-code. It's mostly transparent to client-code which will just silently benefit from it.

Thursday, February 05, 2009 8:36 AM by klaus triendl

# re: Rvalue References: C++0x Features in VC10, Part 2

I'm a big fan of C++, too and I definitely welcome rvalue references because it enables elegant resource management!

But I also agree with Mike that C++ is really painful to read at times, especially the metaprogramming stuff is mind-boggling at least and there's no way to step through runtime-like. And the inclusion model could be definitely better.

On the other hand - why do you scan through an stl header file? You also don't read the source code for a windows api function but rather read the documentation on how to use it.

Templates are complex but how would you do it differently? Even C# has generics and generic programming requires generic thinking, which is inherently complex. Better documentation with concrete examples would be helpful, though.

The good old news is that C++ was designed with programmers' freedom in mind, and that's what I like about C++ - I have control over what I can do and what I'm able to do.

Whether other programmers can understand what I wrote is not the job of the language so much, you can write in every language code that is difficult to grasp, that's my responsibility.

Thursday, February 05, 2009 10:26 AM by Z

# re: Rvalue References: C++0x Features in VC10, Part 2

to off-topic commenters; i for one didn't find this hard at all - as Stephan showed, it's mostly about adding a few lines alongside other copy/assignment operators in existing classes; and as others pointed out, this will be transparent when using STL classes.

if you don't like C++ so be it but please don't come pollute technical articles with useless whines.

to Stephan; thanks a lot for your article. i really enjoyed the first part and i was looking forward to this one in order to fully comprehend what was behind "rvalue references", std::move and friends. i didn't do any other research on the subject, simply waited for your article and it proved to be the perfect source of info i was waiting for!

Thursday, February 05, 2009 11:05 AM by longtime c++/mfc dev

# re: Rvalue References: C++0x Features in VC10, Part 2

Overall, a touch arcane but not too bad.  For library code it can provide a good performance boost, I give it a careful thumbs up.  

IMHO performance + flexibility are substantial differentiators between C++ and other many other languages.  I personally wish the next few cycles of VC++ would focus/integrate more things important to C++ apps: performance (improvements & measurement), reliability (memory/handle leak detection; built-in error/crash reporting),

quality (static code analysis: Win32/MFC/STL), and updated/improved libraies (MFC/STL)

When's the next CTP?

Is MFC to be updated to leverage perf benefits of Rvalue References?

Thursday, February 05, 2009 2:22 PM by JK

# re: Rvalue References: C++0x Features in VC10, Part 2

I agree, better static analysis (boost generates a lot or warnings there). Include graph complexity would be great, some way to easily identify unnecessary or redundant includes.

When's the next installment of this blog series?

Thursday, February 05, 2009 3:33 PM by Mike Diack

# re: Rvalue References: C++0x Features in VC10, Part 2

Please don't get me wrong. I love C++ - Damien Watkins knows that from the phone chat I had with him!

Mike

Thursday, February 05, 2009 3:34 PM by Mike Diack

# re: Rvalue References: C++0x Features in VC10, Part 2

Quick plug: Give Gimpel's PC-Lint a try as a good static analysis tool. I love it.

Mike

Thursday, February 05, 2009 5:54 PM by Stephan T. Lavavej [MSFT]

# re: Rvalue References: C++0x Features in VC10, Part 2

[Stuart Dootson]

> the standard C++ library objects will, I'm sure, have move semantics implemented for them.

> The potential performance benefits make that highly likely.

We're definitely adding support for rvalue references to the C++ Standard Library in VC10.

[BF]

> But when STL came out in the early nineties or whenever it was,

> I took one look at it, and thought, "You've got to be kidding me."

The STL is the best thing about C++!

> To me, when I scan through an STL header file, it might as well be a sheet of lead.

You're not supposed to look at the STL's implementation. (Yes, really!) It's perfectly usable if you look at its interface, as documented in the Standard, etc. The interface is very clean, while the implementation is very complicated. (This is because the STL has to be extremely general, and implements high-powered algorithms and data structures in their own right - for example, deque and map are inherently complicated, not to mention regex.)

[Andreas]

> Seriously, this makes it possible to use std::vector<std::vector<std::string>> without bringing your machine to its knees.

Note that VC8/9's Swaptimization (which I've blogged about at length) already made vector<vector<string> > reallocation cheap. The good news is that move semantics supersede the Swaptimization (which we've ripped out in VC10) and further improve performance.

[Z]

> it's mostly about adding a few lines alongside other copy/assignment operators in existing classes

Yep. This post was long because I didn't want to just say "here, follow these patterns, and they'll magically work". I could say that to beginners learning C++0x from scratch, but not to established C++98/03 programmers who have the ability, right, and responsibility to understand what's really happening.

> to Stephan; thanks a lot for your article.

Thank you for reading it.

[longtime c++/mfc dev]

> performance (improvements & measurement)

Move semantics in the language and libraries, the Concurrency Runtime and Parallel Patterns Library, lambdas (which are more efficient than library solutions like bind() and mem_fn()), and so forth in VC10 should feed your hunger for performance.

> reliability (memory/handle leak detection; built-in error/crash reporting)

The best way to deal with leaktrocity and crashtrocity is to avoid them in the first place. For the former, shared_ptr provides the missing piece of the puzzle.

> Is MFC to be updated to leverage perf benefits of Rvalue References?

In some places, yes. We've already added move constructors and move assignment operators to CComPtr, CComBSTR, and CAdapt.

[JK]

> When's the next installment of this blog series?

I definitely have more material for "C++0x Features in VC10". (I haven't even covered the libraries yet, despite being a library developer!) I don't have a timeframe for Part 3, but writing it certainly won't take me as long as Part 2.

Thursday, February 05, 2009 8:43 PM by Todd Bandrowsky

# Microsoft Gets Its Groove Back

I'm super excited about all of the new things that you are doing with VS 2010 for C++.  For awhile there I thought MS was going to leave us for dead, but, then you went with VS 2008 and blew past my previously favorite KDE, gave MFC a super breath of fresh air with the Fluent stuff, and all of a sudden, the SDK world is alive and well in Windows.  Detractors of MFC really need to just use the wizard and create the sample application with the Office 2007 look for the user interface.  It's just amazing.  It looks fantastic, is very functional, and above all, its pretty damn fast.

You guys are kicking ass, and the world is going to know it.  Keep up the good work, and thank you for the inspiration!

Friday, February 06, 2009 8:03 AM by Visual Studio Hacks

# Visual Studio Links #106

My latest in a series of the weekly, or more often, summary of interesting links I come across related to Visual Studio. John Kilmister has created a XAML for WPF Cheat Sheet and it is available for download (.pdf). Greg Duncan posted a link to the WPF

Friday, February 06, 2009 9:58 AM by longtime c++/mfc dev

# re: Rvalue References: C++0x Features in VC10, Part 2

[Mike]

> Quick plug: Give Gimpel's PC-Lint a try as a good static analysis tool. I love it.

PC-Lint is good stuff with focus on C++  But, a combined analysis on C++ and Win32 (call analysis, usage pattern analysis) would be most excellent - a true product differentiator? or just pie in the sky?

Friday, February 06, 2009 4:57 PM by scorpion007

# re: Rvalue References: C++0x Features in VC10, Part 2

Can you fix your CSS? People on WinXP don't have 'Consolas' installed by default. At least fall back to a generic font-family (i.e. monospace) for source-code. Otherwise I see 'Times New Roman' (browser default) for your snippets. Yuck.

And the previous blogpost had CSS referencing the literal font 'sans-serif' which naturally doesn't exist (it should *not* have quotes), so the generic font family 'sans-serif' was not being used. Aka more ugly fonts.

Saturday, February 07, 2009 11:18 PM by ikk

# re: Rvalue References: C++0x Features in VC10, Part 2

There is a change in semantics being discussed in the C++ committee ("A Safety Problem with RValue References (and what to do about it)").

Is VC10 going to have the right semantics? (whichever is chosen)

Or will Microsoft end up releasing a compiler with pre-standard semantics?

Sorry if this is addressed somewhere in your post. I'm still halfway thru it. ;-)

Sunday, February 08, 2009 3:04 AM by Stephan T. Lavavej [MSFT]

# re: Rvalue References: C++0x Features in VC10, Part 2

[ikk]

> There is a change in semantics being discussed in the C++ committee

I mentioned that at the end of my post.

> Is VC10 going to have the right semantics? (whichever is chosen)

No promises, but I talked to JonCaves, and he said that the front-end changes would be easy. Similarly, the library changes would be easy. The hardest part would be updating our tests. Magic 8 Ball says: Outlook good.

Jonathan also said that this paper would be discussed at the next meeting.

Sunday, February 08, 2009 9:22 PM by Jon Kalb

# re: Rvalue References: C++0x Features in VC10, Part 2

Thank you STL for the great article.

One application of move semantics that you didn't mention occurs to me because I used to teach C++. One thing that beginners want to do, because it comes naturally is declare something like this:

std::string FunctionThatProducesAStringValue();

Why wouldn't you? It is exactly how you would declare this:

int FunctionThatProducesAnIntegralValue();

But because we are experienced C++ programmers we know better and have to explain to users why this isn't efficient and how to do it like this:

void FunctionThatProducesAStringValue(std::string&);

However, with move semantics, the first declaration is no longer inefficient. Since this is a more natural API in man cases this represents a major win for library API designers and users.

To me this is an example of what the committee is trying to do. To preserve backward compatibility, they can't remove anything from the language, but they can still find ways to simplify by addition.

Commenters that believe this is complicating the language are missing the point completely. Here is the burden distribution:

Committee/language implementor:    New complication to deal with

Library writers:                                        Moderate, but straight-forward

                                                                  increase in work

Library users:                                          Libraries are faster with no code

                                                                 change

                                                                 Libraries have more useful APIs

                                                                 without loss of efficiency

Software users:                                       Performance increase without

                                                                 loss of reliability or delays

Kudos to the committee and to language and library implementors for doing us a solid on this one. :)

Monday, February 09, 2009 1:06 PM by JK

# re: Rvalue References: C++0x Features in VC10, Part 2

I definitely want to see more C++ blog posts in the future.

Tuesday, February 10, 2009 12:58 PM by Adam Merz

# re: Rvalue References: C++0x Features in VC10, Part 2

Jon Kalb: The first declaration wasn't inefficient to begin with, as long as the caller only used the return value to construct a new string (RVO/NRVO are pretty dependable) or swap with an existing string, and avoided the assignment operator. But you're right, placing the burden on the caller to always do the right thing is rarely a good design. :-)

Tuesday, February 10, 2009 8:40 PM by pooh

# re: Rvalue References: C++0x Features in VC10, Part 2

Hey, well done once again!

I haven't finished reading this, in fact I only started, but I have a noobie question.

You say <<another way to determine whether an expression is an lvalue is to ask "can I take its address?">>

My intuitive test for lvalue-ness is, "If you can assign something to it, it's an lvalue". Is this incorrect then, even in C++98/03?

Thanks

Wednesday, February 11, 2009 7:33 AM by Stephan T. Lavavej [MSFT]

# re: Rvalue References: C++0x Features in VC10, Part 2

[pooh]

> My intuitive test for lvalue-ness is, "If you can assign something to it, it's an lvalue". Is this incorrect then, even in C++98/03?

Yes.

const int c = 299792458; // c is a const lvalue

c = 300000000; // won't compile

You'll notice that I carefully avoided mentioning "lvalue"'s derivation from "what can appear on the left side of an assignment", because that hasn't been true since const was added to C.

Even the Standard suggests "locator value" as a better expansion than "left value". If I had my way I'd probably call them pvalues and tvalues, for "persistent" and "temporary".

I originally wrote this as an E-mail to a Microsoft-internal mailing list, where I devoted a whole section to explaining the history of lvalues and rvalues in C and C++. This was more confusing than enlightening, and in the worst possible place too (at the beginning, where I am already asking readers to set aside their boredom for a few minutes in exchange for something that will grant them ultimate power). What matters is the current meaning of the terms, not their history, so when I rewrote this for VCBlog I removed the history lesson and devoted more time to demonstrating modifiable/const lvalues/rvalues.

Wednesday, February 11, 2009 6:41 PM by int19h

# re: Rvalue References: C++0x Features in VC10, Part 2

Thank you for another great blog post, Stephan - it's the reason why this blog is one of those I check daily :)

In the previous installment of this post series, in comments, I raised the issue of how TR1 classes are handled for VC10. Here's how it went:

[int19h]  As I understand, you'll be moving all the stuff from TR1 that makes reappearance in C++0x into namespace std. What about the new stuff, such as std::unique_ptr?

[STL] We're dragging TR1 components into namespace std with using-declarations.

[int19h] Erm... I might be wrong, but isn't it going to Break Things (for template specialization purposes etc), unless you have "strong using" implemented, and are using it for that purpose?

[STL] That's a good question. I'll ask.

Any news on that front?

Thursday, February 12, 2009 3:40 PM by Stephan T. Lavavej [MSFT]

# re: Rvalue References: C++0x Features in VC10, Part 2

int19h:

I talked to Dinkumware, and the using-declarations almost certainly affect user specializations. (It's not the end of the world - you just have to switch which namespace you're specializing in.) We're doing it this way because it minimizes the changes to our codebase and the impact on TR1 users (who are more numerous than our zero C++0x users). At some point in the future, we may reverse things (declaring everything in std and dragging selected components into tr1).

Eventually, I'd like to remove TR1 (it differs from C++0x in several ways, and supporting both is more work). However, VC10 will certainly still support TR1.

Friday, February 13, 2009 3:10 PM by Termékinformációk fejlesztőknek

# Visual Studio Standard Edition (csak frissítés/MSDN nélküli, dobozos – FPP változatban kapható)

[Nacsa Sándor, 2009. január 20. – február 12.] Komplett fejlesztő környezet Windows kliens és web alkalmazások

Saturday, February 14, 2009 1:50 AM by int19h

# re: Rvalue References: C++0x Features in VC10, Part 2

> It's not the end of the world - you just have to switch which namespace you're specializing in.

While true, this introduces a potential point of incompatibility between implementations. I'm fairly sure that g++ will have it working correctly straight away, for example, because they have "strong using" already, which allows them to pull the same trick you do entirely transparently for the client.

Saturday, February 14, 2009 10:59 AM by Aubrey

# Very Interesting the new thing

C and C++ were devised for great performance but some flaws have been around for years. It seems that most of this stuff is fixed in the new Standard which is awesome!!

I've been working with containers for a while and the temporaries thing worried me because it really hurt performance. Now It's definitively solve at last.

From my point of view its a pity that TR1 improvements like smart pointers, bind and function were not release before. They have been around since almost the appearance of the last standard and work just fine with it.

My greetings to Stephen for the article I have learn a lot from it. I've been using expressions like

MyType & ob = Function(...);

very often because it used to save the creation of a temporary object without known it breaks the standard. Now it seems that the compiler have been improved enought to make

MyType ob = Function(...);

work in the same way.

To make all my dreams be fullfil for now the libraries team should reimplement the valarray class taking advantage of SSE technology.

Great Job!!

Saturday, February 14, 2009 6:12 PM by Joe

# re: Rvalue References: C++0x Features in VC10, Part 2

This article cracked me up. Many of us solved these problems years ago and in much more elegant ways. Unfortunately, C++ has largely become an academic language with extremely confusing and contradictory constructs which add little to writing small, fast, efficient code. The reality is that most problems in C++ can be avoided by keeping your code simply and straightforward. For example, anyone who writes:

string result = s1 + " " + s2;

is an idiot. This is true for C# as well. Even writing:

string result = s1;

result += " ";

result += s2;

is dumb and results in poor, bloated performance. Truth is, if all you want is these two strings concatenated together, just use simply string copies and concatenations.

The point is that I see developers using STL and C++ concepts so cavalierly, I wonder why they even bother with C++. If small size and fast operation doesn't matter, why not use C# or Java or, heaven forbid, VB? If, on the other hand, small size and speed matter than learn to write small, fast code deliberately without having to guess what the C++ compiler will do.

Sunday, February 15, 2009 12:51 AM by mikeb

# re: Rvalue References: C++0x Features in VC10, Part 2

Stephan:

Thanks very much for this great primer on rvalue references.

Among other things, I liked the way you clearly summed up lvalues vs. rvalues:

------------------

Lvalueness versus rvalueness doesn't care about what an expression does, it cares about what an expression names (something persistent or something temporary).

------------------

Sunday, February 15, 2009 6:09 AM by praveen.

# very nice.

so nice way of programming its interesting i love knowing and reading this program.

Sunday, February 15, 2009 7:55 AM by jackonlyone

# re: Rvalue References: C++0x Features in VC10, Part 2

Thanks very much for this update with VC++ 10.0, we will move all our components to use this new feature.

Jack

-----------------------------------------------

For high quality flow/diagram MFC/C++ Visio 12 Like visualization Source Code, download XD++ at: http://www.ucancode.net

Tuesday, February 17, 2009 8:19 PM by int19h

# re: Rvalue References: C++0x Features in VC10, Part 2

> I've been using expressions like

>

> MyType & ob = Function(...);

>

> very often because it used to save the creation of a temporary object

without known it breaks the standard.

Um, surely you know that, more often than not, RVO will kick in anyway so there is no temporary created there, even with today's compilers?

Thursday, February 19, 2009 7:21 PM by morovia

# re: Rvalue References: C++0x Features in VC10, Part 2

Will VC10 support variable length array (VLA)? I really need that feature badly. Other C/C++ compilers support VLA.

Thursday, February 19, 2009 7:54 PM by Stephan T. Lavavej [MSFT]

# re: Rvalue References: C++0x Features in VC10, Part 2

[morovia]

> Will VC10 support variable length array (VLA)?

No; that is a C99 language feature.

> I really need that feature badly.

Why? C++ has std::vector.

Friday, February 20, 2009 2:03 PM by Niels Dekker

# re: Rvalue References: C++0x Features in VC10, Part 2

Thanks for the blog, Stephan! Just a few little remarks of mine:

It's interesting to get to know why morovia wants to have VLA so badly, but it looks like this is indeed recognized as a significant "missing feature" in C++, as "dynarray" is planned to be added to a future version of the Standard C++ Library. http://www.open-std.org/JTC1/sc22/wg21/docs/papers/2008/n2648.html

There's a tiny little bug regarding the exception-safety of the copy-assignment operator of your remote_integer, at least if "new int(*other.m_p)" might ever throw...  Which is highly theoretical, I hope!  :-)

FYI, I've just submitted a paper that intends to bring back that historical notion of lvalues being allowed at the left side of an assignment, while keeping the rvalues at the right. (As much as reasonable, of course!)  Cowritten with Daniel Kruegler: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2819.html

Just my two cents, Niels

Friday, February 20, 2009 4:27 PM by Stephan T. Lavavej [MSFT]

# re: Rvalue References: C++0x Features in VC10, Part 2

[Niels Dekker]

> There's a tiny little bug regarding the exception-safety of the copy-assignment operator of your remote_integer

Yep, my bad. I should have provided at least the basic guarantee, by setting m_p = NULL after deleting it.

> FYI, I've just submitted a paper that intends to bring back that historical notion of lvalues being allowed at the left side of an assignment, while keeping the rvalues at the right.

That appears to be a worthy proposal, but it doesn't allow "lvalue means left" to be taught. Const lvalues can't be assigned to.

Friday, February 20, 2009 4:58 PM by Niels Dekker

# re: Rvalue References: C++0x Features in VC10, Part 2

> That appears to be a worthy proposal

Thank you!

> but it doesn't allow "lvalue means left" to be taught. Const lvalues can't be assigned to.

Our proposal certainly isn't going to make any more const lvalues assignable, so my previous comment was oversimplified.  FWIW, certain types /do/ allow assignment to const (both lvalue and rvalue), including std::slice_array and std::indirect_array. We've excluded those particular types from the proposal, as they're typically /intended/ to support assignment to an rvalue.

Friday, February 20, 2009 5:23 PM by Stephan T. Lavavej [MSFT]

# re: Rvalue References: C++0x Features in VC10, Part 2

Yes - I should have clarified that I was talking about built-ins.

Thursday, February 26, 2009 11:14 AM by Ralphos

# re: Rvalue References: C++0x Features in VC10, Part 2

[Mike]

> Quick plug: Give Gimpel's PC-Lint a try as a good static analysis tool. I love it.

Another quick plug:

If you want to use PC Link with Visual Studio then you should look at Visual Lint at

 http://www.riverblade.co.uk/

Saturday, February 28, 2009 7:57 AM by pooh

# re: Rvalue References: C++0x Features in VC10, Part 2

OK, finished. As I said, excellent article. I can't say I understood everything, but I think I get the idea.

I think it would have been nice to summarise the benefits and common idioms enabled by rvalue refs for mere mortals - and I suspect it would have spared us some flames. I'll have a stab at it here, please correct me if I'm wrong or I missed any other benefits.

1. If, by means of std::move, you implement move copy ctor and move assignment operator for MyType (and you should), users of that class need not worry ever again about the performance of writing things like

MyType myObj = f();

because move semantics kick in and save you a copy where RVO and NRVO do not - and incidentally behaving in a deterministic manner, unlike NRVO, but I might not be up to date on the subject.

2. Similarly, unnecessary copies of temporary objects in chained expressions are avoided.

3. Most of the standard library is now move-semantics-aware, and therefore having e.g. a standard container (what else?) of MyTypes is more performant when things like reallocations occur.

4. perfect forwarding is now enabled and supported by std::forward - for those who care

Have I been an attentive boy?

Thanks

Sunday, March 01, 2009 1:03 AM by Yoco Xiao

# re: Rvalue References: C++0x Features in VC10, Part 2

Hi Stephan, I am a CS graduated student in TsingHwa University in Taiwan. I had read this great artical and learned a lot. Thank you for your great job.

I would like to share it to Taiwan's CS students and C++ programmer. Can I translate this artical into Traditional Chainese, and post it to some forum(telnet.ptt.cc) in Taiwan and my own blog(endlesschildhood.blogspot.com)? Of cause I will indicate the source of this article.

Thanks for your great work again.

peter.xiau at gmail

Sunday, March 01, 2009 9:05 PM by Ahmed Charles

# re: Rvalue References: C++0x Features in VC10, Part 2

I was just wondering if this is a reasonable implementation of a copy assignment operator that gives the strong guarantee?

   remote_integer& operator=(const remote_integer& other) {

       cout << "Copy assignment operator." << endl;

       if (this != &other) {

           if (m_p) {

               if (other.m_p) {

                   *m_p = *other.m_p;

               } else {

                   delete m_p;

                   m_p = NULL;

               }

           } else {

               if (other.m_p) {

                   m_p = new int(*other.m_p);

               }

           }

       }

       return *this;

   }

I hope that formats properly.

Monday, March 02, 2009 6:33 PM by Sean Hunt

# re: Rvalue References: C++0x Features in VC10, Part 2

As a big supporter of C++1x (sorry guys, it's not coming out this year), I am very happy to see that you guys are implementing these things so quickly. C++1x stands to be one of the fastest-accepted new standards, as MSVC, GCC, EDG, and CodeGear are all implementing various C++1x features, and I'm already using some of them in my own code, and starting to feel annoyed when I can't (std::tie, I love you).

I also do a fair bit of porting, which brings me to my first point:

> I should mention that VC has an evil extension that allows this, but if you compile with /W4 , it warns when the evil extension is activated.

I can't help but scream NONONONONONONO. I'm not an expert on what all the warning flags mean (so I could be overreacting here), but somehow I suspect /W4 is not common, and thus that many programmers code assuming this extension is normal, inflicting headaches on people such as myself trying to compile with different compilers. It's like VC6 all over again.

std::tr1 vs. std is another interesting issue. Once again, I would very much push you guys to implement properly forwarding bind, etc. immediately. Since tr1 will cease (at least as far as my understanding of ISO procedure goes) to be normative when C++1x is release, if you aren't providing separate options for picking C++1x mode and appropriate macros in the headers (as GCC does), then perhaps you would find it easiest to do this:

namespace std

{

 namespace tr1 = ::std;

}

This allows specializations to be present in std or std::tr1. Of course, it gives a couple of other funny results (like std::tr1::tr1::tr1::vector<int> being legal), but if you aren't going to go the compile-switch route (which I would highly recommend!), this is probably the most elegant solution.

The last complaint is something you've probably heard before, but I would just like to reiterate: Please implement C++1x standard attributes. Please. Nothing will hurt adoption of C++1x more than a major compiler leaving out a major feature. You can put all your existing attributes in an attribute-namespace like "ms", and then the entire world will celebrate :)

One last thing: If VC10 actually implements exception specifications and two-stage name lookup, I will be happy, because those are two useful features (yes, I just called C++ exception specifications useful!) that you sadly don't have yet. The first is rather minor, as I use it mainly to help debugging, but the second is important - if you look at Boost code you may understand why it is I care so much that compilers implement templates The Right Way. Export would be nice too, but that was a pipe dream 10 years ago and hasn't changed one bit.

Monday, March 02, 2009 9:23 PM by pooh

# re: Rvalue References: C++0x Features in VC10, Part 2

@Sean Hunt

From Stephan's first article we learn that

"The Standardization Committee hopes that they'll be finished in 2009, making it C++09; the joke is that if it slips to 2010 or later, the 'x' will be hexadecimal."

Monday, March 02, 2009 11:04 PM by Stephan T. Lavavej [MSFT]

# re: Rvalue References: C++0x Features in VC10, Part 2

[pooh]

> As I said, excellent article. I can't say I understood everything, but I think I get the idea.

Awesome.

> I think it would have been nice to summarise the benefits and common idioms enabled by rvalue refs for mere mortals

I summarized the benefits (performance and genericity) in my introduction. I also clearly identified the common patterns/idioms with my section titles, and bolded the relevant parts of my self-contained examples.

Although I strongly prefer self-contained examples over snippets when explaining anything in detail, I could have presented the relevant parts as snippets in my introduction, followed by the self-contained examples. I'll keep that in mind for the future.

> I'll have a stab at it here, please correct me if I'm wrong or I missed any other benefits.

#1: Correct. The RVO and NRVO are applied at the compiler's discretion; you can see in my examples how I got the compiler to give up and not apply them. Also, the RVO/NRVO apply only to construction, not to assignment.

#2: Correct. Assuming that the operators, etc. in the chain are aware of move semantics.

#3: Correct. Actually, all of the Standard Library will be aware of move semantics. Any deficiencies are bugs.

#4: Correct. Library authors take advantage of perfect forwarding, and library users automatically benefit. This is unlike move semantics where library users have to provide move constructors and move assignment operators for their types in order to get them picked up by the library.

[Yoco Xiao]

> Thank you for your great job.

You're welcome.

> Can I translate this artical into Traditional Chainese

Yes, that's fine. Please link to the original article and indicate that your translation is unofficial.

[Ahmed Charles]

> I was just wondering if this is a reasonable implementation of a copy assignment operator that gives the strong guarantee?

Yep. Thanks for writing it up.

[Sean Hunt]

> starting to feel annoyed when I can't (std::tie, I love you).

VC9 SP1 contains std::tr1::tie(); perhaps you're talking about another compiler.

>> I should mention that VC has an evil extension that allows this

> I can't help but scream NONONONONONONO.

I don't like that extension. (The phrases "abomination" and "enemy of humanity" come to mind.) Hence the "evil".

> I'm not an expert on what all the warning flags mean (so I could be overreacting here), but somehow I suspect /W4 is not common,

VC's /W4 ("warning level 4") is the rough equivalent of GCC's -Wall -Wextra . That is, it attempts to provide all of the commonly useful warnings. There are some useful warnings that it doesn't provide, and some of the warnings that it does provide aren't very useful, but by and large it'll find lots of badness and waste little of your time. VC's /Wall ("all warnings") does what it says on the box: it enables all warnings (some of which are extremely noisy). VC's headers are /W4-clean but not /Wall-clean.

Everyone should compile with /W4 at a minimum. Although I don't use IDEs, I believe that devenv defaults to /W3 .

> and thus that many programmers code assuming this extension is normal,

> inflicting headaches on people such as myself trying to compile with different compilers.

Hence the "evil".

> It's like VC6 all over again.

It's almost certainly *from* VC6. To be clear, this evil extension is NOT new to VC10 - it's very old.

> I would very much push you guys to implement properly forwarding bind, etc. immediately.

I've already opened a bug to use rvalue references in <functional> (which will also simplify its implementation).

> Since tr1 will cease (at least as far as my understanding of ISO procedure goes) to be normative when C++1x is release

TR1 was never normative; it was not an International Standard.

> Please implement C++1x standard attributes.

Thanks for the request. However, this won't be implemented in VC10. We really didn't have time to implement everything (including the feature that I'm drooling over, variadic templates). In particular, attributes weren't finalized until very recently (they weren't integrated into the Working Paper as of the N2705 status report).

> One last thing: If VC10 actually implements exception specifications and two-stage name lookup

It won't, sorry.

Thursday, March 05, 2009 10:08 AM by Peter Harris

# re: C99 *still* not supported?!?

>> Will VC10 support variable length array (VLA)?

> No; that is a C99 language feature.

Where do I put in a feature request for C99 support in VC10?

I really don't want to have to move to mingw (from VC9).

Friday, March 06, 2009 9:36 AM by JK

# re: Rvalue References: C++0x Features in VC10, Part 2

@Peter Harris

Good luck.  I've requested C99 and they won't do it.  C99 would be a big win for Microsoft.

Wednesday, April 22, 2009 1:16 PM by Visual C++ Team Blog

# decltype: C++0x Features in VC10, Part 3

Part 1 of this series covered lambdas , auto , and static_assert . Part 2 of this series covered rvalue

Thursday, May 21, 2009 12:45 PM by ComponentGear.com Feed

# VC 10: Stephan T. Lavavej and Damien Watkins - Inside STL

Visual Studio 2010 Beta 1 introduces a number of exciting new features for the C++ developer as we include

Monday, May 25, 2009 11:54 AM by Visual C++ Team Blog

# STL Breaking Changes in Visual Studio 2010 Beta 1

Visual Studio 2010 Beta 1 is now available for download. I've recently blogged about how Visual C++ in

New Comments to this post are disabled
 
Page view tracker