Stack Exchange Network

Stack Exchange network consists of 183 Q&A communities including Stack Overflow , the largest, most trusted online community for developers to learn, share their knowledge, and build their careers.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

What are R-value references used for?

I'm trying to dig deep into C++ and really learn the nuances of the language but one thing I've found to be really confusing is the R-Value reference. The whole double ampersand thing to be clear (in case I'm getting the terminology wrong). It's not a big deal as it doesn't seem to come up often but it seems like it could occasionally be useful to know.

Keltek's user avatar

  • Stack Overflow has an extremely popular question with a legendary answer about this. –  5gon12eder Commented Oct 11, 2015 at 3:20

3 Answers 3

A common use case is the “move constructor,” invoked when an object is being copied from a temporary that’s about to expire (a rvalue). An example is foo = bar + baz; where bar + baz is a temporary on the right-hand side of an assignment statement.

Before rvalue references, objects that made deep copies of their contents had to construct their data in one place, then copy it all to another, then destroy the first copy. This was slow and wasteful.

When the source is a rvalue reference, the move constructor or assignment operator will typically make a shallow copy of the source’s data, then remove the source’s references so that it will be an empty shell when it’s deleted a moment later and the data will now have a new owner. This is safe, because a rvalue reference won’t be used for anything else before it’s destroyed, much faster, and saves memory.

Davislor's user avatar

  • 1 An interesting predecessor to this is that, when dealing with a function that returned an object, the compiler has been allowed to omit the copy function (i.e. it was implementation defined whether the copy occurred or not). Move mechanics using R-values is the natural extension of this, allowing for guaranteed moves (rather than implementation defined ones), allowing it in more circumstances, and allowing user-specified behaviors for it. –  Cort Ammon Commented Oct 9, 2015 at 7:11
  • Really? You mean before C++11, it was copies everywhere? How did people write performance critical code back then? –  wingerse Commented Dec 21, 2018 at 15:37
  • 2 @WingerSendon Several ways, but one approach was a programmatic constructor. That is, you might express object foo = bar + baz; by constructing it in place as object foo( sum_constructor(), bar, baz ); with no copies. –  Davislor Commented Dec 21, 2018 at 16:44

lvalue : A value that resides in memory (heap or stack) and addressable.

rvalue : A value that's not lvalue.

It resides only on the right side of an assignment expression such as a literal or a temporary which is intended to be non-modifiable.

here is very good tutorial about it:

http://www.bogotobogo.com/cplusplus/C11/4_C11_Rvalue_Lvalue.php

Robert Harvey's user avatar

  • I think a little more detail is needed here. One can have return MyObject(); which appears to be a stack variable and therefor an lvalue. However, the compiler will normally turn it into an rvalue and either construct it in-place at the call site, or return it as a movable rvalue. –  user22815 Commented Oct 9, 2015 at 17:15

I found this very hard to understand at first too. If you think about r-values as temporary, unnamed instances it helps. For example

functionCall( SomeObject{ 0 } );

In this case

SomeObject{ 0 }

is a constructor call creating a temporary, unnamed instance of the SomeObject class. If you make the signature of

functionCall( SomeObject&& x )

then it will only accept temporary unnamed objects.

Now, once you understand this, then you need to realize that a named, non-temporary object can be converted into an R-value either automatically by the compiler, or explicitly by the programmer ( std::move ).

If you move a named object std::move( x ) then x can no longer be used because it's "guts" (memory) have been moved.

The primary use for r-values as a language feature is to move objects into and out of functions without copying.

Matthew James Briggs's user avatar

Your Answer

Reminder: Answers generated by artificial intelligence tools are not allowed on Software Engineering Stack Exchange. Learn more

Sign up or log in

Post as a guest.

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy .

Not the answer you're looking for? Browse other questions tagged c++ c++11 or ask your own question .

  • The Overflow Blog
  • From PHP to JavaScript to Kubernetes: how one backend engineer evolved over time
  • Featured on Meta
  • We've made changes to our Terms of Service & Privacy Policy - July 2024
  • Bringing clarity to status tag usage on meta sites

Hot Network Questions

  • How can I address my colleague communicating with us via chatGPT?
  • Using ExplSyntax with tasks to create a custom \item command - Making \item backwards compatible
  • Is a probabilistic implementation of unitaries invertible?
  • Immutability across programming languages
  • Cannot remove old solder
  • Copper bonding jumper around new whole-house water filter system
  • Why are volumes of revolution typically taught in Calculus 2 and not Calculus 3?
  • How is the grammar of this sentence explained?
  • CCTV control box connector
  • Which class is the language MAX-CLIQUE in?
  • How does one go about writing papers as a nobody?
  • Hiding all UI elements within a blender window with the python API
  • Help Identify SMD Part Marking ATPJ
  • How to algebraically "decouple" a pair of linear equations without finding eigenvectors?
  • How to ensure a BSD licensed open source project is not closed in the future?
  • Miracle Miracle Octad Generator Generator
  • How can I draw water level in a cylinder like this?
  • Difference between 頁 and ページ
  • Will this be the first time that there are more people aboad the ISS than seats in docked spacecraft?
  • Reduce String Length With Thread Safety & Concurrency
  • What food plants/algae are viable to grow 9,000 meters beneath the sea, if given plenty of light and air?
  • Books to read as an intro to existential philosophy
  • Will fire still burn well in my atmosphere?: concentration vs partial pressure
  • Flight left while checked in passenger queued for boarding

assignment operator rvalue

cppreference.com

Assignment operators.

(C++20)
(C++20)
(C++11)
(C++20)
(C++17)
(C++11)
(C++11)
General topics
(C++11)
-
-expression
- block
  
(C++11)
(C++11)
(C++11)
/
(C++11)
    
(C++11)
expression
pointer
specifier
specifier (C++11)
specifier (C++11)
(C++11)
(C++11)
(C++11)
(C++11)
General
(lvalue, rvalue, xvalue)
(sequence points)
(C++11)
Literals
including
(C++11)
(C++11)
Operators
:
:  
:
:
:
:
:
, , , , , , , , , ,
, , ,
, , , , , , , , , , , ,
, ,
, , , , , , (C++20)
, , , , , ,
, ,

Assignment operators modify the value of the object.

Operator name  Syntax  Prototype examples (for class T)
Inside class definition Outside class definition
simple assignment Yes T& T::operator =(const T2& b);
addition assignment Yes T& T::operator +=(const T2& b); T& operator +=(T& a, const T2& b);
subtraction assignment Yes T& T::operator -=(const T2& b); T& operator -=(T& a, const T2& b);
multiplication assignment Yes T& T::operator *=(const T2& b); T& operator *=(T& a, const T2& b);
division assignment Yes T& T::operator /=(const T2& b); T& operator /=(T& a, const T2& b);
remainder assignment Yes T& T::operator %=(const T2& b); T& operator %=(T& a, const T2& b);
bitwise AND assignment Yes T& T::operator &=(const T2& b); T& operator &=(T& a, const T2& b);
bitwise OR assignment Yes T& T::operator |=(const T2& b); T& operator |=(T& a, const T2& b);
bitwise XOR assignment Yes T& T::operator ^=(const T2& b); T& operator ^=(T& a, const T2& b);
bitwise left shift assignment Yes T& T::operator <<=(const T2& b); T& operator <<=(T& a, const T2& b);
bitwise right shift assignment Yes T& T::operator >>=(const T2& b); T& operator >>=(T& a, const T2& b);

this, and most also return *this so that the user-defined operators can be used in the same manner as the built-ins. However, in a user-defined operator overload, any type can be used as return type (including void). can be any type including
Explanation Builtin direct assignment Example Builtin compound assignment Example Defect reports See also

[ edit ] Explanation

copy assignment operator replaces the contents of the object a with a copy of the contents of b ( b is not modified). For class types, this is a special member function, described in copy assignment operator .

operator replaces the contents of the object with the contents of , avoiding copying if possible ( may be modified). For class types, this is a special member function, described in .

(since C++11)

For non-class types, copy and move assignment are indistinguishable and are referred to as direct assignment .

compound assignment operators replace the contents of the object a with the result of a binary operation between the previous value of a and the value of b .

[ edit ] Builtin direct assignment

The direct assignment expressions have the form

lhs rhs (1)
lhs (2) (since C++11)
lhs rhs (3) (since C++11)

For the built-in operator, lhs may have any non-const scalar type and rhs must be implicitly convertible to the type of lhs .

The direct assignment operator expects a modifiable lvalue as its left operand and an rvalue expression or a braced-init-list (since C++11) as its right operand, and returns an lvalue identifying the left operand after modification. The result is a bit-field if the left operand is a bit-field.

For non-class types, the right operand is first implicitly converted to the cv-unqualified type of the left operand, and then its value is copied into the object identified by left operand.

When the left operand has reference type, the assignment operator modifies the referred-to object.

If the left and the right operands identify overlapping objects, the behavior is undefined (unless the overlap is exact and the type is the same)

If the right operand is a

has scalar type, {} is equivalent to E1 = T{}, where is the type of . {E2} is equivalent to E1 = T{E2}, where is the type of . has class type, the syntax E1 = {args...} generates a call to the assignment operator with the as the argument, which then selects the appropriate assignment operator following the rules of . Note that, if a non-template assignment operator from some non-class type is available, it is preferred over the copy/move assignment in because to non-class is an , which outranks the user-defined conversion from to a class type.
(since C++11)

Using an lvalue of volatile-qualified non-class type as left operand of built-in direct assignment operator is deprecated, unless the assignment expression appears in an or is a .

(since C++20)

In overload resolution against user-defined operators , for every type T , the following function signatures participate in overload resolution:

& operator=(T*&, T*);
volatile & operator=(T*volatile &, T*);

For every enumeration or pointer to member type T , optionally volatile-qualified, the following function signature participates in overload resolution:

operator=(T&, T);

For every pair A1 and A2, where A1 is an arithmetic type (optionally volatile-qualified) and A2 is a promoted arithmetic type, the following function signature participates in overload resolution:

operator=(A1&, A2);

[ edit ] Example

[ edit ] builtin compound assignment.

The compound assignment expressions have the form

lhs op rhs (1)
lhs op (2) (since C++11)
lhs op rhs (3) (since C++11)
op - one of *=, /= %=, += -=, <<=, >>=, &=, ^=, |=
lhs - for the built-in operator, lhs may have any arithmetic type, except when op is += or -=, which also accept pointer types with the same restrictions as + and -
rhs - for the built-in operator, rhs must be implicitly convertible to lhs

The behavior of every builtin compound-assignment expression E1 op = E2 (where E1 is a modifiable lvalue expression and E2 is an rvalue expression or a braced-init-list (since C++11) ) is exactly the same as the behavior of the expression E1 = E1 op E2 , except that the expression E1 is evaluated only once and that it behaves as a single operation with respect to indeterminately-sequenced function calls (e.g. in f ( a + = b, g ( ) ) , the += is either not started at all or is completed as seen from inside g ( ) ).

In overload resolution against user-defined operators , for every pair A1 and A2, where A1 is an arithmetic type (optionally volatile-qualified) and A2 is a promoted arithmetic type, the following function signatures participate in overload resolution:

operator*=(A1&, A2);
operator/=(A1&, A2);
operator+=(A1&, A2);
operator-=(A1&, A2);

For every pair I1 and I2, where I1 is an integral type (optionally volatile-qualified) and I2 is a promoted integral type, the following function signatures participate in overload resolution:

operator%=(I1&, I2);
operator<<=(I1&, I2);
operator>>=(I1&, I2);
operator&=(I1&, I2);
operator^=(I1&, I2);
operator|=(I1&, I2);

For every optionally cv-qualified object type T , the following function signatures participate in overload resolution:

& operator+=(T*&, );
& operator-=(T*&, );
volatile & operator+=(T*volatile &, );
volatile & operator-=(T*volatile &, );
This section is incomplete
Reason: no example

[ edit ] Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
C++11 for assignments to class type objects, the right operand
could be an initializer list only when the assignment
is defined by a user-defined assignment operator
removed user-defined
assignment constraint
C++11 E1 = {E2} was equivalent to E1 = T(E2)
( is the type of ), this introduced a C-style cast
it is equivalent
to E1 = T{E2}
C++20 bitwise compound assignment operators for volatile types
were deprecated while being useful for some platforms
they are not deprecated
C++20 compound assignment operators for volatile types
were inconsistently deprecated
none of them is deprecated

[ edit ] See also

Operator precedence

Operator overloading

Common operators

a = b
a += b
a -= b
a *= b
a /= b
a %= b
a &= b
a |= b
a ^= b
a <<= b
a >>= b

++a
--a
a++
a--

+a
-a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b

!a
a && b
a || b

a == b
a != b
a < b
a > b
a <= b
a >= b
a <=> b

a[...]
*a
&a
a->b
a.b
a->*b
a.*b

function call
a(...)
comma
a, b
conditional
a ? b : c
Special operators

converts one type to another related type
converts within inheritance hierarchies
adds or removes -qualifiers
converts type to unrelated type
converts one type to another by a mix of , , and
creates objects with dynamic storage duration
destructs objects previously created by the new expression and releases obtained memory area
queries the size of a type
queries the size of a (since C++11)
queries the type information of a type
checks if an expression can throw an exception (since C++11)
queries alignment requirements of a type (since C++11)

for Assignment operators
  • Todo no example
  • Recent changes
  • Offline version
  • What links here
  • Related changes
  • Upload file
  • Special pages
  • Printable version
  • Permanent link
  • Page information
  • In other languages
  • This page was last modified on 21 November 2022, at 04:25.
  • This page has been accessed 357,612 times.
  • Privacy policy
  • About cppreference.com
  • Disclaimers

Powered by MediaWiki

Logo for Rebus Press

Want to create or adapt books like this? Learn more about how Pressbooks supports open publishing practices.

Lvalue and Rvalue

Kenneth Leroy Busbee

Some programming languages use the idea of l-values and r-values , deriving from the typical mode of evaluation on the left and right hand side of an assignment statement. An lvalue refers to an object that persists beyond a single expression. An rvalue is a temporary value that does not persist beyond the expression that uses it. [1]

Lvalue and Rvalue refer to the left and right side of the assignment operator. The  Lvalue  (pronounced: L value) concept refers to the requirement that the operand on the left side of the assignment operator is modifiable, usually a variable.  Rvalue  concept pulls or fetches the value of the expression or operand on the right side of the assignment operator. Some examples:

The value 39 is pulled or fetched (Rvalue) and stored into the variable named age (Lvalue); destroying the value previously stored in that variable.

If the expression has a variable or named constant on the right side of the assignment operator, it would pull or fetch the value stored in the variable or constant. The value 18 is pulled or fetched from the variable named voting_age and stored into the variable named age.

If the expression is a test expression or Boolean expression, the concept is still an Rvalue one. The value in the identifier named age is pulled or fetched and used in the relational comparison of less than.

This is illegal because the identifier JACK_BENNYS_AGE does not have Lvalue properties. It is not a modifiable data object, because it is a constant.

Some uses of the Lvalue and Rvalue can be confusing in languages that support increment and decrement operators. Consider:

Postfix increment says to use my existing value then when you are done with the other operators; increment me. Thus, the first use of the oldest variable is an Rvalue context where the existing value of 55 is pulled or fetched and then assigned to the variable age; an Lvalue context. The second use of the oldest variable is an Lvalue context wherein the value of the oldest is incremented from 55 to 56.

  • cnx.org: Programming Fundamentals – A Modular Structured Approach using C++
  • Wikipedia: Value (computer science) ↵

Programming Fundamentals Copyright © 2018 by Kenneth Leroy Busbee is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License , except where otherwise noted.

Share This Book

This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Rvalue reference declarator: &&

  • 7 contributors

Holds a reference to an rvalue expression.

rvalue-reference-type-id :   type-specifier-seq && attribute-specifier-seq opt ptr-abstract-declarator opt

Rvalue references enable you to distinguish an lvalue from an rvalue. Lvalue references and rvalue references are syntactically and semantically similar, but they follow slightly different rules. For more information about lvalues and rvalues, see Lvalues and Rvalues . For more information about lvalue references, see Lvalue Reference Declarator: & .

The following sections describe how rvalue references support the implementation of move semantics and perfect forwarding .

Move semantics

Rvalue references support the implementation of move semantics , which can significantly increase the performance of your applications. Move semantics enables you to write code that transfers resources (such as dynamically allocated memory) from one object to another. Move semantics works because it enables transfer of resources from temporary objects: ones that can't be referenced elsewhere in the program.

To implement move semantics, you typically provide a move constructor, and optionally a move assignment operator ( operator= ), to your class. Copy and assignment operations whose sources are rvalues then automatically take advantage of move semantics. Unlike the default copy constructor, the compiler doesn't provide a default move constructor. For more information about how to write and use a move constructor, see Move constructors and move assignment operators .

You can also overload ordinary functions and operators to take advantage of move semantics. Visual Studio 2010 introduces move semantics into the C++ Standard Library. For example, the string class implements operations that use move semantics. Consider the following example that concatenates several strings and prints the result:

Before Visual Studio 2010, each call to operator+ allocates and returns a new temporary string object (an rvalue). operator+ can't append one string to the other because it doesn't know whether the source strings are lvalues or rvalues. If the source strings are both lvalues, they might be referenced elsewhere in the program, and so must not be modified. You can modify operator+ to take rvalues by using rvalue references, which can't be referenced elsewhere in the program. With this change, operator+ can now append one string to another. The change significantly reduces the number of dynamic memory allocations that the string class must make. For more information about the string class, see basic_string Class .

Move semantics also helps when the compiler can't use Return Value Optimization (RVO) or Named Return Value Optimization (NRVO). In these cases, the compiler calls the move constructor if the type defines it.

To better understand move semantics, consider the example of inserting an element into a vector object. If the capacity of the vector object is exceeded, the vector object must reallocate enough memory for its elements, and then copy each element to another memory location to make room for the inserted element. When an insertion operation copies an element, it first creates a new element. Then it calls the copy constructor to copy the data from the previous element to the new element. Finally, it destroys the previous element. Move semantics enables you to move objects directly without having to make expensive memory allocation and copy operations.

To take advantage of move semantics in the vector example, you can write a move constructor to move data from one object to another.

For more information about the introduction of move semantics into the C++ Standard Library in Visual Studio 2010, see C++ Standard Library .

Perfect forwarding

Perfect forwarding reduces the need for overloaded functions and helps avoid the forwarding problem. The forwarding problem can occur when you write a generic function that takes references as its parameters. If it passes (or forwards ) these parameters to another function, for example, if it takes a parameter of type const T& , then the called function can't modify the value of that parameter. If the generic function takes a parameter of type T& , then the function can't be called by using an rvalue (such as a temporary object or integer literal).

Ordinarily, to solve this problem, you must provide overloaded versions of the generic function that take both T& and const T& for each of its parameters. As a result, the number of overloaded functions increases exponentially with the number of parameters. Rvalue references enable you to write one version of a function that accepts arbitrary arguments. Then that function can forward them to another function as if the other function had been called directly.

Consider the following example that declares four types, W , X , Y , and Z . The constructor for each type takes a different combination of const and non- const lvalue references as its parameters.

Suppose you want to write a generic function that generates objects. The following example shows one way to write this function:

The following example shows a valid call to the factory function:

However, the following example doesn't contain a valid call to the factory function. It's because factory takes lvalue references that are modifiable as its parameters, but it's called by using rvalues:

Ordinarily, to solve this problem, you must create an overloaded version of the factory function for every combination of A& and const A& parameters. Rvalue references enable you to write one version of the factory function, as shown in the following example:

This example uses rvalue references as the parameters to the factory function. The purpose of the std::forward function is to forward the parameters of the factory function to the constructor of the template class.

The following example shows the main function that uses the revised factory function to create instances of the W , X , Y , and Z classes. The revised factory function forwards its parameters (either lvalues or rvalues) to the appropriate class constructor.

Properties of rvalue references

You can overload a function to take an lvalue reference and an rvalue reference.

By overloading a function to take a const lvalue reference or an rvalue reference, you can write code that distinguishes between non-modifiable objects (lvalues) and modifiable temporary values (rvalues). You can pass an object to a function that takes an rvalue reference unless the object is marked as const . The following example shows the function f , which is overloaded to take an lvalue reference and an rvalue reference. The main function calls f with both lvalues and an rvalue.

This example produces the following output:

In this example, the first call to f passes a local variable (an lvalue) as its argument. The second call to f passes a temporary object as its argument. Because the temporary object can't be referenced elsewhere in the program, the call binds to the overloaded version of f that takes an rvalue reference, which is free to modify the object.

The compiler treats a named rvalue reference as an lvalue and an unnamed rvalue reference as an rvalue.

Functions that take an rvalue reference as a parameter treat the parameter as an lvalue in the body of the function. The compiler treats a named rvalue reference as an lvalue. It's because a named object can be referenced by several parts of a program. It's dangerous to allow multiple parts of a program to modify or remove resources from that object. For example, if multiple parts of a program try to transfer resources from the same object, only the first transfer succeeds.

The following example shows the function g , which is overloaded to take an lvalue reference and an rvalue reference. The function f takes an rvalue reference as its parameter (a named rvalue reference) and returns an rvalue reference (an unnamed rvalue reference). In the call to g from f , overload resolution selects the version of g that takes an lvalue reference because the body of f treats its parameter as an lvalue. In the call to g from main , overload resolution selects the version of g that takes an rvalue reference because f returns an rvalue reference.

In the example, the main function passes an rvalue to f . The body of f treats its named parameter as an lvalue. The call from f to g binds the parameter to an lvalue reference (the first overloaded version of g ).

  • You can cast an lvalue to an rvalue reference.

The C++ Standard Library std::move function enables you to convert an object to an rvalue reference to that object. You can also use the static_cast keyword to cast an lvalue to an rvalue reference, as shown in the following example:

Function templates deduce their template argument types and then use reference collapsing rules.

A function template that passes (or forwards ) its parameters to another function is a common pattern. It's important to understand how template type deduction works for function templates that take rvalue references.

If the function argument is an rvalue, the compiler deduces the argument to be an rvalue reference. For example, assume you pass an rvalue reference to an object of type X to a function template that takes type T&& as its parameter. Template argument deduction deduces T to be X , so the parameter has type X&& . If the function argument is an lvalue or const lvalue, the compiler deduces its type to be an lvalue reference or const lvalue reference of that type.

The following example declares one structure template and then specializes it for various reference types. The print_type_and_value function takes an rvalue reference as its parameter and forwards it to the appropriate specialized version of the S::print method. The main function demonstrates the various ways to call the S::print method.

To resolve each call to the print_type_and_value function, the compiler first does template argument deduction. The compiler then applies reference collapsing rules when it replaces the parameter types with the deduced template arguments. For example, passing the local variable s1 to the print_type_and_value function causes the compiler to produce the following function signature:

The compiler uses reference collapsing rules to reduce the signature:

This version of the print_type_and_value function then forwards its parameter to the correct specialized version of the S::print method.

The following table summarizes the reference collapsing rules for template argument type deduction:

Expanded type Collapsed type

Template argument deduction is an important element of implementing perfect forwarding. The Perfect forwarding section describes perfect forwarding in more detail.

Rvalue references distinguish lvalues from rvalues. To improve the performance of your applications, they can eliminate the need for unnecessary memory allocations and copy operations. They also enable you to write a function that accepts arbitrary arguments. That function can forward them to another function as if the other function had been called directly.

Expressions with unary operators Lvalue reference declarator: & Lvalues and rvalues Move constructors and move assignment operators (C++) C++ Standard Library

Was this page helpful?

Additional resources

Join us on Facebook!

We use cookies to personalise content and ads, to provide social media features and to analyse our traffic. By using our site, you acknowledge that you have read and understand our Privacy Policy , and our Terms of Service . Your use of this site is subject to these policies and terms. | ok, got it

— Written by Triangles on June 02, 2018 • updated on February 15, 2019 • ID 63 —

C++ rvalue references and move semantics for beginners

A collection of personal notes and thoughts on rvalue references, their role in move semantics and how they can significantly increase the performance of your applications.

In my previous article Understanding the meaning of lvalues and rvalues in C++ I had the chance to explain to myself the logic behind rvalues . The core idea is that in C++ you will find such temporary, short-lived values that you cannot alter in any way.

Surprisingly, modern C++ (C++0x and greater) has introduced rvalue references : a new type that can bind to temporary objects, giving you the ability to modify them. Why?

Let's begin this journey with a little brush up of temporary values:

On line (1) the literal constant 666 is an rvalue: it has no specific memory address, except for some temporary register while the program is running. It needs to be stored in a lvalue ( x ) to be useful. Line (4) is similar, but here the rvalue is not hard-coded, rather it is being returned by the function getString() . However, as in line (1), the temporary object must be stored in an lvalue ( s4 ) to be meaningful.

Lines (2) and (3) seem more subtle: the compiler has to create a temporary object to hold the result of the + operator. Being a temporary one, the output is of course an rvalue that must be stored somewhere. And that's what I did by putting the results in y and s3 respectively.

Introducing the magic of rvalue references

The traditional C++ rules say that you are allowed to take the address of an rvalue only if you store it in a const (immutable) variable. More technically, you are allowed to bind a const lvalue to an rvalue . Consider the following example:

The first operation is wrong: it's an invalid initialization of non-const reference of type int& from an rvalue of type int . The second line is the way to go. Of course, being x a constant, you can't alter it.

C++0x has introduced a new type called rvalue reference , denoted by placing a double ampersand && after some type. Such rvalue reference lets you modify the value of a temporary object: it's like removing the const attribute in the second line above!

Let's play a bit with this new toy:

Here I create two simple strings s1 and s2 . I join them and I put the result (a temporary string, i.e. an rvalue) into std::string&& s_rref . Now s_rref is a reference to a temporary object, or an rvalue reference. There are no const around it, so I'm free to modify the temporary string to my needs. This wouldn't be possible without rvalue references and its double ampersand notation. To better distinguish it, we refer to traditional C++ references (the single-ampersand one) as lvalue references .

This might seem useless at a first glance. However rvalue references pave the way for the implementation of move semantics , a technique which can significantly increase the performance of your applications.

Move semantics, the scenic route

Move semantics is a new way of moving resources around in an optimal way by avoiding unnecessary copies of temporary objects, based on rvalue references. In my opinion, the best way to understand what move semantics is about is to build a wrapper class around a dynamic resource (i.e. a dynamically allocated pointer) and keep track of it as it moves in and out functions. Keep in mind however that move semantics does not apply only to classes!

That said, let's take a look at the following example:

It is a naive class that handles a dynamic chunk of memory: nothing fancy so far, except for the allocation part. When you choose to manage the memory yourself you should follow the so-called Rule of Three . This rule states that if your class defines one or more of the following methods it should probably explicitly define all three:

  • destructor;
  • copy constructor;
  • copy assignment operator.

A C++ compiler will generate them by default if needed, in addition to the constructor and other functions we don't care about right now. Unfortunately the default versions are just "not enough" when your class deals with dynamic resources. Indeed, the compiler couldn't generate a constructor like the one in the example above: it doesn't know anything about the logic of our class.

Implementing the copy constructor

Let's stick to the Rule of Three and implement the copy constructor first. As you may know, the copy constructor is used to create a new object from another existing object. For example:

How a copy constructor would look like:

Here I'm initializing a new Holder object out of the existing one passed in as other : I create a new array of the same size (1) and then I copy the actual data from other.m_data to m_data (i.e. this.m_data ) (2).

Implementing the assignment operator

It's now time for the assignment operator, used to replace an existing object with another existing object. For example:

How an assigment operator would look like:

First of all a little protection against self-assignment (1). Then, since we are replacing the content of this class with another one, let's wipe out the current data (2). What's left is just the same code we wrote in the copy constructor. By convention a reference to this class is returned (3).

The key point of the copy constructor and the assignment operator is that they both receive a const reference to an object in input and make a copy out of it for the class they belong to. The object in input, being a constant reference, is of course left untouched.

The limitations of our current class design

Our class is good to go, but it lacks of some serious optimization. Consider the following function:

It returns a Holder object by value . We know that when a function returns an object by value, the compiler has to create a temporary — yet fully-fledged — object (rvalue). Now, our Holder is a heavy-weight object due to its internal memory allocation, which is a very expensive task: returning such things by value with our current class design would trigger multiple expensive memory allocations, which is rarely a great idea. How come? Consider this:

A temporary object coming out from createHolder() is passed to the copy constructor. According to our current design, the copy constructor allocates its own m_data pointer by copying the data from the temporary object. Two expensive memory allocations: a) during the creation of the temporary, b) during the actual object copy-construct operation.

The same copy procedure occurs within the assignment operator:

The code inside our assignment operator wipes the memory out and then reallocates it from scratch by copying the data from the temporary object. Yet another two expensive memory allocations: a) during the creation of the temporary, b) in the actual object assignment operator.

Too many expensive copies! We already have a fully-fledged object, the temporary and short-lived one returning from createHolder() , built for us by the compiler: it's an rvalue that will fade away with no use at the next instruction: why, during the construction/assignment stages, don't we steal — or move the allocated data inside the temporary object instead of making an expensive copy out of it?

In the old days of C++ there was no way to optimize this out: returning heavy-weight objects by value was simply a no-go. Fortunately in C++11 and greater we are allowed (and encouraged) to do this, by improving our current Holder class with move semantics. In a nutshell, we will steal existing data from temporary objects instead of making useless clones. Don't copy, just move , because moving is always cheaper.

Implementing move semantics with rvalue references

Let's spice up our class with move semantics: the idea is to add new versions of the copy constructor and assignment operator so that they can take a temporary object in input to steal data from. To steal data means to modify the object the data belongs to: how can we modify a temporary object? By using rvalue references!

At this point we naturally follow another C++ pattern called the Rule of Five . It's an extension to the Rule of Three seen before and it states that any class for which move semantics are desirable, has to declare two additional member functions:

  • the move constructor — to construct new objects by stealing data from temporaries;
  • the move assignment operator — to replace existing objects by stealing data from temporaries.

Implementing the move constructor

A typical move constructor:

It takes in input an rvalue reference to another Holder object. This is the key part: being an rvalue reference, we can modify it. So let's steal its data first (1), then set it to null (2). No deep copies here, we have just moved resources around! It's important to set the rvalue reference data to some valid state (2) to prevent it from being accidentally deleted when the temporary object dies: our Holder destructor calls delete[] m_data , remember? In general, for reasons that will become more clear in a few paragraphs, it's a good idea to always leave the objects being stolen from in some well-defined state.

Implementing the move assignment operator

The move assignment operator follows the same logic:

We steal data (2) from the other object coming in as an rvalue reference, after a cleanup of the existing resources (1). Let's not forget to put the temporary object to some valid state (3) as we did in the move constructor. Everything else is just regular assignment operator duty.

Now that we have our new methods in place, the compiler is smart enough to detect whether you are creating an object with a temporary value (rvalue) or a regular one (lvalue) and trigger the proper constructor/operator accordingly. For example:

Where and when move semantics apply

Move semantics provide a smarter way of passing heavy-weight things around. You create your heavy-weight resource only once and then you move it where needed in a natural way. As I said before, move semantics is not only about classes. You can make use of it whenever you need to change the ownership of a resource across multiple areas of your application. However keep in mind that, unlike a pointer, you are not sharing anything: if object A steals data from object B, data in object B no longer exists, thus is no longer valid. As we know this is not a problem when dealing with temporary objects, but you can also steal from regular ones. We will see how shortly.

I tried your code: the move constructor never gets called!

That's right. If you run the last snippet above you will notice how the move constructor does not get called during (1). The regular constructor is called instead: this is due to a trick called Return Value Optimization (RVO) . Modern compilers are able to detect that you are returning an object by value, and they apply a sort of return shortcut to avoid useless copies.

You can tell the compiler to bypass such optimization: for example, GCC supports the -fno-elide-constructors flag. Compile the program with such flag enabled and run it again: the amount of constructor/destructor calls will increase noticeably.

Why should I care implementing move semantics if the RVO does its optimization job by default?

RVO is only about return values (output), not function parameters (input). There are many places where you may pass movable objects as input parameters, which would make the move constructor and the move assignment operator come into play, if implemented. The most important one: the Standard Library. During the upgrade to C++11 all the algorithms and containers in there were extended to support move semantics. So if you use the Standard Library with classes that follow the Rule of Five you will gain an important optimization boost.

Can I move lvalues?

Yes you can, with the utility function std::move from the Standard Library. It is used to convert an lvalue into an rvalue. Say we want to steal from an lvalue:

This will not work: since h2 receives an lvalue in input, the copy constructor is being triggered. We need to force the move constructor on h2 in order to make it steal from h1 , so:

Here std::move has converted the lvalue h1 into an rvalue: the compiler sees such rvalue in input and then triggers the move constructor on h2 . The object h2 will steal data from h1 during its construction stage.

Mind that at this point h1 is a hollow object. However, we did a good thing when in our move constructor we set the stolen object's data to a valid state ( other.m_data = nullptr , remember?). Now you may want to reuse h1 , test it in some way or let it go out of scope without causing nasty crashes.

Final notes and possible improvements

This article is way too long and I've only scratched the surface of move semantics. What follows is a quick list of additional concepts I will further investigate in the future.

We did RAII in our basic Holder example

Resource Acquisition Is Initialization (RAII) is a C++ technique where you wrap a class around a resource (file, socket, database connection, allocated memory, ...). The resource is initialized in the class constructor and cleaned up in the class destructor. This way you are sure to avoid resource leaks. More information: here .

Mark you move constructors and move assignment operators with noexcept

The C++11 keyword noexcept means "this function will never throw exceptions". It is used to optimize things out. Some people say that move constructors and move assignment operators should never throw. Rationale: you should not allocate memory or call other code in there. You should only copy data and set the other object to null, i.e. non-throwing operations. More information: here , here .

Further optimizations and stronger exception safety with copy-and-swap idiom

All the constructors/assignment operators in the Holder class are full of duplicate code, which is not so great. Moreover, if the allocation throws an exception in the copy assignment operator the source object might be left in a bad state. The copy-and-swap idiom fixes both issues, at the cost of adding a new method to the class. More information: here , here .

Perfect forwarding

This technique allows you to move your data across multiple template and non-template functions without wrong type conversions (i.e. perfectly). More information: here , here .

Stack Overflow - When is an rvalue evaluated? ( link ) Mikw's C++11 blog - Lesso #5: Move Semantics ( link ) Artima - A Brief Introduction to Rvalue References ( link ) Stack Overflow - C++11 rvalues and move semantics confusion (return statement) ( link ) Cpp-patterns - The rule of five ( link ) open-std.org - A Brief Introduction to Rvalue References ( link ) Microsoft - Rvalue Reference Declarator: && ( link ) Wikipedia - Rule of three (C++ programming) ( link ) Stack Overflow - What are all the member-functions created by compiler for a class? Does that happen all the time? ( link ) cplusplus.com - Copy constructors, assignment operators, and exception safe assignment ( link ) Stack Overflow - What is the copy-and-swap idiom? ( link ) Wikipedia - Assignment operator (C++) ( link ) Stack Overflow - When the move constructor is actually called if we have (N)RVO? ( link ) cppreference.com - The rule of three/five/zero ( link ) cprogramming.com - Move semantics and rvalue references in C++11 ( link ) Stack Overflow - What is std::move(), and when should it be used? ( link )

Assignment operators

(C++20)
(C++20)
(C++11)
(C++11)
(C++11)
(C++17)
General topics
(C++11)
-
-expression
- block
(C++11)
(C++11)
(C++11)
/
(C++11)
(C++11)
Expressions
expression
pointer
specifier
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
General
(lvalue, rvalue, xvalue)
(sequence points)
(C++11)
Literals
including
(C++11)
(C++11)
Operators
: , , , , , , , , , ,
: , , ,
: , , , , , , , , , , , ,
: , ,
: , , , , , , (C++20)
: , , , , , ,
: , ,
(C++20)
(C++17)
(C++11)
(C++11)
Conversions
,

Assignment operators modify the value of the object.

Operator name Syntax Prototype examples (for class T)
Inside class definition Outside class definition
simple assignment Yes T& T::operator =(const T2& b);
addition assignment Yes T& T::operator +=(const T2& b); T& operator +=(T& a, const T2& b);
subtraction assignment Yes T& T::operator -=(const T2& b); T& operator -=(T& a, const T2& b);
multiplication assignment Yes T& T::operator *=(const T2& b); T& operator *=(T& a, const T2& b);
division assignment Yes T& T::operator /=(const T2& b); T& operator /=(T& a, const T2& b);
modulo assignment Yes T& T::operator %=(const T2& b); T& operator %=(T& a, const T2& b);
bitwise AND assignment Yes T& T::operator &=(const T2& b); T& operator &=(T& a, const T2& b);
bitwise OR assignment Yes T& T::operator |=(const T2& b); T& operator |=(T& a, const T2& b);
bitwise XOR assignment Yes T& T::operator ^=(const T2& b); T& operator ^=(T& a, const T2& b);
bitwise left shift assignment Yes T& T::operator <<=(const T2& b); T& operator <<=(T& a, const T2& b);
bitwise right shift assignment Yes T& T::operator >>=(const T2& b); T& operator >>=(T& a, const T2& b);

this, and most also return *this so that the user-defined operators can be used in the same manner as the built-ins. However, in a user-defined operator overload, any type can be used as return type (including void). can be any type including

Explanation

copy assignment operator replaces the contents of the object a with a copy of the contents of b ( b is not modified). For class types, this is a special member function, described in copy assignment operator .

move assignment operator replaces the contents of the object a with the contents of b , avoiding copying if possible ( b may be modified). For class types, this is a special member function, described in move assignment operator . (since C++11)

For non-class types, copy and move assignment are indistinguishable and are referred to as direct assignment .

compound assignment operators replace the contents of the object a with the result of a binary operation between the previous value of a and the value of b .

Builtin direct assignment

The direct assignment expressions have the form

lhs rhs (1)
lhs = {} (2) (since C++11)
lhs = {rhs} (3) (since C++11)

For the built-in operator, lhs may have any non-const scalar type and rhs must be implicitly convertible to the type of lhs .

The direct assignment operator expects a modifiable lvalue as its left operand and an rvalue expression or a braced-init-list (since C++11) as its right operand, and returns an lvalue identifying the left operand after modification.

For non-class types, the right operand is first implicitly converted to the cv-unqualified type of the left operand, and then its value is copied into the object identified by left operand.

When the left operand has reference type, the assignment operator modifies the referred-to object.

If the left and the right operands identify overlapping objects, the behavior is undefined (unless the overlap is exact and the type is the same)

If the right operand is a

has scalar type, {} is equivalent to E1 = T{}, where is the type of . {E2} is equivalent to E1 = T{E2}, where is the type of . has class type, the syntax E1 = {args...} generates a call to the assignment operator with the as the argument, which then selects the appropriate assignment operator following the rules of . Note that, if a non-template assignment operator from some non-class type is available, it is preferred over the copy/move assignment in because to non-class is an , which outranks the user-defined conversion from to a class type.
(since C++11)

In overload resolution against user-defined operators , for every type T , the following function signatures participate in overload resolution:

& operator=(T*&, T*);
volatile & operator=(T*volatile &, T*);

For every enumeration or pointer to member type T , optionally volatile-qualified, the following function signature participates in overload resolution:

operator=(T&, T );

For every pair A1 and A2, where A1 is an arithmetic type (optionally volatile-qualified) and A2 is a promoted arithmetic type, the following function signature participates in overload resolution:

operator=(A1&, A2);

Builtin compound assignment

The compound assignment expressions have the form

lhs op rhs (1)
lhs op {} (2) (since C++11)
lhs op {rhs} (3) (since C++11)
op - one of *=, /= %=, += -=, <<=, >>=, &=, ^=, |=
lhs - for the built-in operator, lhs may have any arithmetic type, except when op is += or -=, which also accept pointer types with the same restrictions as + and -
rhs - for the built-in operator, rhs must be implicitly convertible to lhs

The behavior of every builtin compound-assignment expression E1 op = E2 (where E1 is a modifiable lvalue expression and E2 is an rvalue expression or a braced-init-list (since C++11) ) is exactly the same as the behavior of the expression E1 = E1 op E2 , except that the expression E1 is evaluated only once and that it behaves as a single operation with respect to indeterminately-sequenced function calls (e.g. in f ( a + = b, g ( ) ) , the += is either not started at all or is completed as seen from inside g ( ) ).

In overload resolution against user-defined operators , for every pair A1 and A2, where A1 is an arithmetic type (optionally volatile-qualified) and A2 is a promoted arithmetic type, the following function signatures participate in overload resolution:

operator*=(A1&, A2);
operator/=(A1&, A2);
operator+=(A1&, A2);
operator-=(A1&, A2);

For every pair I1 and I2, where I1 is an integral type (optionally volatile-qualified) and I2 is a promoted integral type, the following function signatures participate in overload resolution:

operator%=(I1&, I2);
operator<<=(I1&, I2);
operator>>=(I1&, I2);
operator&=(I1&, I2);
operator^=(I1&, I2);
operator|=(I1&, I2);

For every optionally cv-qualified object type T , the following function signatures participate in overload resolution:

& operator+=(T*&, );
& operator-=(T*&, );
volatile & operator+=(T*volatile &, );
volatile & operator-=(T*volatile &, );
This section is incomplete
Reason: no example

Operator precedence

Operator overloading

Common operators

a = b
a += b
a -= b
a *= b
a /= b
a %= b
a &= b
a |= b
a ^= b
a <<= b
a >>= b

++a
--a
a++
a--

+a
-a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b

!a
a && b
a || b

a == b
a != b
a < b
a > b
a <= b
a >= b
a <=> b

a[b]
*a
&a
a->b
a.b
a->*b
a.*b

a(...)
a, b
? :

Special operators

converts one type to another related type
converts within inheritance hierarchies
adds or removes qualifiers
converts type to unrelated type
converts one type to another by a mix of , , and
creates objects with dynamic storage duration
destructs objects previously created by the new expression and releases obtained memory area
queries the size of a type
queries the size of a (since C++11)
queries the type information of a type
checks if an expression can throw an exception (since C++11)
queries alignment requirements of a type (since C++11)

assignment operator rvalue

Understanding lvalues and rvalues in C and C++

The terms lvalue and rvalue are not something one runs into often in C/C++ programming, but when one does, it's usually not immediately clear what they mean. The most common place to run into these terms are in compiler error & warning messages. For example, compiling the following with gcc :

True, this code is somewhat perverse and not something you'd write, but the error message mentions lvalue , which is not a term one usually finds in C/C++ tutorials. Another example is compiling this code with g++ :

Now the error is:

Here again, the error mentions some mysterious rvalue . So what do lvalue and rvalue mean in C and C++? This is what I intend to explore in this article.

A simple definition

This section presents an intentionally simplified definition of lvalues and rvalues . The rest of the article will elaborate on this definition.

An lvalue ( locator value ) represents an object that occupies some identifiable location in memory (i.e. has an address).

rvalues are defined by exclusion, by saying that every expression is either an lvalue or an rvalue . Therefore, from the above definition of lvalue , an rvalue is an expression that does not represent an object occupying some identifiable location in memory.

Basic examples

The terms as defined above may appear vague, which is why it's important to see some simple examples right away.

Let's assume we have an integer variable defined and assigned to:

An assignment expects an lvalue as its left operand, and var is an lvalue, because it is an object with an identifiable memory location. On the other hand, the following are invalid:

Neither the constant 4 , nor the expression var + 1 are lvalues (which makes them rvalues). They're not lvalues because both are temporary results of expressions, which don't have an identifiable memory location (i.e. they can just reside in some temporary register for the duration of the computation). Therefore, assigning to them makes no semantic sense - there's nowhere to assign to.

So it should now be clear what the error message in the first code snippet means. foo returns a temporary value which is an rvalue. Attempting to assign to it is an error, so when seeing foo() = 2; the compiler complains that it expected to see an lvalue on the left-hand-side of the assignment statement.

Not all assignments to results of function calls are invalid, however. For example, C++ references make this possible:

Here foo returns a reference, which is an lvalue , so it can be assigned to. Actually, the ability of C++ to return lvalues from functions is important for implementing some overloaded operators. One common example is overloading the brackets operator [] in classes that implement some kind of lookup access. std::map does this:

The assignment mymap[10] works because the non-const overload of std::map::operator[] returns a reference that can be assigned to.

Modifiable lvalues

Initially when lvalues were defined for C, it literally meant "values suitable for left-hand-side of assignment". Later, however, when ISO C added the const keyword, this definition had to be refined. After all:

So a further refinement had to be added. Not all lvalues can be assigned to. Those that can are called modifiable lvalues . Formally, the C99 standard defines modifiable lvalues as:

[...] an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.

Conversions between lvalues and rvalues

Generally speaking, language constructs operating on object values require rvalues as arguments. For example, the binary addition operator '+' takes two rvalues as arguments and returns an rvalue:

As we've seen earlier, a and b are both lvalues. Therefore, in the third line, they undergo an implicit lvalue-to-rvalue conversion . All lvalues that aren't arrays, functions or of incomplete types can be converted thus to rvalues.

What about the other direction? Can rvalues be converted to lvalues? Of course not! This would violate the very nature of an lvalue according to its definition [1] .

This doesn't mean that lvalues can't be produced from rvalues by more explicit means. For example, the unary '*' (dereference) operator takes an rvalue argument but produces an lvalue as a result. Consider this valid code:

Conversely, the unary address-of operator '&' takes an lvalue argument and produces an rvalue:

The ampersand plays another role in C++ - it allows to define reference types. These are called "lvalue references". Non-const lvalue references cannot be assigned rvalues, since that would require an invalid rvalue-to-lvalue conversion:

Constant lvalue references can be assigned rvalues. Since they're constant, the value can't be modified through the reference and hence there's no problem of modifying an rvalue. This makes possible the very common C++ idiom of accepting values by constant references into functions, which avoids unnecessary copying and construction of temporary objects.

CV-qualified rvalues

If we read carefully the portion of the C++ standard discussing lvalue-to-rvalue conversions [2] , we notice it says:

An lvalue (3.10) of a non-function, non-array type T can be converted to an rvalue. [...] If T is a non-class type, the type of the rvalue is the cv-unqualified version of T. Otherwise, the type of the rvalue is T.

What is this "cv-unqualified" thing? CV-qualifier is a term used to describe const and volatile type qualifiers.

From section 3.9.3:

Each type which is a cv-unqualified complete or incomplete object type or is void (3.9) has three corresponding cv-qualified versions of its type: a const-qualified version, a volatile-qualified version, and a const-volatile-qualified version. [...] The cv-qualified or cv-unqualified versions of a type are distinct types; however, they shall have the same representation and alignment requirements (3.9)

But what has this got to do with rvalues? Well, in C, rvalues never have cv-qualified types. Only lvalues do. In C++, on the other hand, class rvalues can have cv-qualified types, but built-in types (like int ) can't. Consider this example:

The second call in main actually calls the foo () const method of A , because the type returned by cbar is const A , which is distinct from A . This is exactly what's meant by the last sentence in the quote mentioned earlier. Note also that the return value from cbar is an rvalue. So this is an example of a cv-qualified rvalue in action.

Rvalue references (C++11)

Rvalue references and the related concept of move semantics is one of the most powerful new features the C++11 standard introduces to the language. A full discussion of the feature is way beyond the scope of this humble article [3] , but I still want to provide a simple example, because I think it's a good place to demonstrate how an understanding of what lvalues and rvalues are aids our ability to reason about non-trivial language concepts.

I've just spent a good part of this article explaining that one of the main differences between lvalues and rvalues is that lvalues can be modified, and rvalues can't. Well, C++11 adds a crucial twist to this distinction, by allowing us to have references to rvalues and thus modify them, in some special circumstances.

As an example, consider a simplistic implementation of a dynamic "integer vector". I'm showing just the relevant methods here:

So, we have the usual constructor, destructor, copy constructor and copy assignment operator [4] defined, all using a logging function to let us know when they're actually called.

Let's run some simple code, which copies the contents of v1 into v2 :

What this prints is:

Makes sense - this faithfully represents what's going on inside operator= . But suppose that we want to assign some rvalue to v2 :

Although here I just assign a freshly constructed vector, it's just a demonstration of a more general case where some temporary rvalue is being built and then assigned to v2 (this can happen for some function returning a vector, for example). What gets printed now is this:

Ouch, this looks like a lot of work. In particular, it has one extra pair of constructor/destructor calls to create and then destroy the temporary object. And this is a shame, because inside the copy assignment operator, another temporary copy is being created and destroyed. That's extra work, for nothing.

Well, no more. C++11 gives us rvalue references with which we can implement "move semantics", and in particular a "move assignment operator" [5] . Let's add another operator= to Intvec :

The && syntax is the new rvalue reference . It does exactly what it sounds it does - gives us a reference to an rvalue, which is going to be destroyed after the call. We can use this fact to just "steal" the internals of the rvalue - it won't need them anyway! This prints:

What happens here is that our new move assignment operator is invoked since an rvalue gets assigned to v2 . The constructor and destructor calls are still needed for the temporary object that's created by Intvec(33) , but another temporary inside the assignment operator is no longer needed. The operator simply switches the rvalue's internal buffer with its own, arranging it so the rvalue's destructor will release our object's own buffer, which is no longer used. Neat.

I'll just mention once again that this example is only the tip of the iceberg on move semantics and rvalue references. As you can probably guess, it's a complex subject with a lot of special cases and gotchas to consider. My point here was to demonstrate a very interesting application of the difference between lvalues and rvalues in C++. The compiler obviously knows when some entity is an rvalue, and can arrange to invoke the correct constructor at compile time.

One can write a lot of C++ code without being concerned with the issue of rvalues vs. lvalues, dismissing them as weird compiler jargon in certain error messages. However, as this article aimed to show, getting a better grasp of this topic can aid in a deeper understanding of certain C++ code constructs, and make parts of the C++ spec and discussions between language experts more intelligible.

Also, in the new C++ spec this topic becomes even more important, because C++11's introduction of rvalue references and move semantics. To really grok this new feature of the language, a solid understanding of what rvalues and lvalues are becomes crucial.

assignment operator rvalue

rvalues can be assigned to lvalues explicitly. The lack of implicit conversion means that rvalues cannot be used in places where lvalues are expected.
That's section 4.1 in the new C++11 standard draft.
You can find a lot of material on this topic by simply googling "rvalue references". Some resources I personally found useful: , , and .
This a canonical implementation of a copy assignment operator, from the point of view of exception safety. By using the copy constructor and then the non-throwing , it makes sure that no intermediate state with uninitialized memory can arise if exceptions are thrown.
So now you know why I was keeping referring to my as "copy assignment operator". In C++11, the distinction becomes important.

For comments, please send me an email .

assignment operator rvalue

  • Latest Articles
  • Top Articles
  • Posting/Update Guidelines
  • Article Help Forum

assignment operator rvalue

  • View Unanswered Questions
  • View All Questions
  • View C# questions
  • View C++ questions
  • View Javascript questions
  • View Visual Basic questions
  • View .NET questions
  • CodeProject.AI Server
  • All Message Boards...
  • Running a Business
  • Sales / Marketing
  • Collaboration / Beta Testing
  • Work Issues
  • Design and Architecture
  • Artificial Intelligence
  • Internet of Things
  • ATL / WTL / STL
  • Managed C++/CLI
  • Objective-C and Swift
  • System Admin
  • Hosting and Servers
  • Linux Programming
  • .NET (Core and Framework)
  • Visual Basic
  • Web Development
  • Site Bugs / Suggestions
  • Spam and Abuse Watch
  • Competitions
  • The Insider Newsletter
  • The Daily Build Newsletter
  • Newsletter archive
  • CodeProject Stuff
  • Most Valuable Professionals
  • The Lounge  
  • The CodeProject Blog
  • Where I Am: Member Photos
  • The Insider News
  • The Weird & The Wonderful
  • What is 'CodeProject'?
  • General FAQ
  • Ask a Question
  • Bugs and Suggestions

assignment operator rvalue

The new C++ 11 rvalue reference && and why you should start using it

assignment operator rvalue

Introduction                      

This is an attempt to explain new && reference present in latest versions of compilers as part of implementing the new C++ 11 standard.  Such as those shipping with Visual studio 10-11-12 and gcc 4.3-4, or beautiful fast( equally if not more) open-source alternative to gcc Clang.     

Why you should start using it ? In short : Nontrivial performance gain.    For example   inserts to std::vector (or in fact any array creation) will not cost huge amount of allocs/copies anymore .   

But before we go to detail of new c++ 11 "Move semantics"  we need to understand the core of problem. We need to understand why performance problem of c++ language with = assign operation often resulting to useless alloc/copy exists.    

We live in the world where we need to work with a lot of data. We need to store and process it in an effective manner. Problem with c and c++ is that we got used to do it ineffectively.    

The real price for copying via = was small since pointers are just numbers (containing memory addresses). But with objects the story is different.   

How the performance problem started 

Now I will try to keep this short but it is important to really understand why && operator was born.

In C++ working with  references become prevalent since they are safer alternative to pointers and very importantly new language features such as automatic call of constructors or  destructors (automatic cleanup) or operators worked only with them.    

Now make no mistake  References are internally still just pointers but this time made little bit safer and automatically dereferenced. Period. How are they made safer? Well. They can never contain invalid data since the only assignment to them is allowed during their declaration and only to existing statically declared data. But whenever you pass reference to function in fact it is still just pointer that is internally pushed on stack.  

 C++ wanted to make our life easier by doing routine pointer de/referencing for us (and hiding this pointer wizardry from us). Illusion of passing or working with objects instead of pointers to them was so perfect that many lost sense what is actually reference and what is object.

Exactly this ambiguity had unfortunate side effect that led us into believing that this

is just is safer alternative to this    

We just got rid of unsafe pointers in favor of safer references like everywhere else.But more importantly this has given us automatic call of constructors destructor and operators. Right?  Wrong. There is no such thing as array of references in C++.  No pointer magic behind the scene is going on this time like it is within functions. So what we actually created is algorithmically very bad decision. We created an array of objects stored by value. And no. Removing * from declaration doesn't automatically make pointer variable reference. From performance point of view there is really no alternative to array of pointers . With big objects containing statically declared structured data there is big performance difference when creating sorting  searching and mainly reallocating 100mb array of values. Then it is with few bytes of pointers to them.  It pretty much kills the possibility to work within processor cache with as much data as possible. So we sort/search/reallocate etc on the speed of main memory order of magnitude slower instead of cpu cache which we now uselessly pollute with unimportant object data. With objects containing big dynamic data situation is better when sorting etc. But still bad when assigned to array. Since then we need to allocate and copy large chunks of dynamic mem plus rest of object. But I would say mentioned reallocation is worst consequence of storing object by values. 

So every time you think of "nah lets put vector<large-object> there". Your memory requirements will be twice of what they need to be and your performance abysmal due to fact that whole allocated mem will be uselessly moved around after each realloc. One would think that this is just price we indeed agreed to pay for generic approaches and laziness.  But by my opinion this is the price for storing objects instead of pointers for the oo reasons (automatic constructors destructors operators)   mentioned above.    

But back to the topic. As we remember assign = "actually" copies data. And this leads us to another big performance problem with arrays.    

How to efficiently build array of large objects. Suppose you wanna build a city full of skyscrapers and you obviously (due to large scale) can't afford any waste of time or resources.    

Now think of city as an array analogy. So what you actually do is you  "create" building inside city 

  • you allocate large space for building inside of city     
  • and obviously you just build building in this space.     

But thanks to our habit of using = operator to store object pointers to array in good old C.  Its only natural that we attempt to use the same  "create and assign"  paradigm with references too;  In other words. We simply got used to it and what's worse every c++ book teaches as to do it this ineffective way.     

  • you allocate large temporary space outside city // = Skyscraper();    
  • you build skyscraper in this temporary space  
  • you allocate the same large space inside city    // city[1].operator = (Skyscraper &in)   
  • you use large amount uf trucks gas and time to move this skyscraper to space in city   

It manifests with arrays so strongly because of sheer number of ineffective assignments. In case of our benchmark its 500 assignments but if you do any reference assignment that can be handled by moving (as explained later) and not copying in loop with 500 iterations you have basically the same problem.

Yes there are but they are nor intuitive or obvious and are hack like in nature. Now if C++ did allow us to invoke specialized constructor and create objects using already allocated space inside array(that was allocated just once for all elements by one efficient alloc). Then this could saved zillion of allocs/copies most of us usually do by assigning new objects via = ; 

Still. There are some ways to do it.  You can for example move all your initialization code to method and invoke it on array element explicitly.       

  • you allocate large space for building inside city     
  • you build building in this space.     

Hurray. The problem introduced by using = is gone.   

That means now it doesn't matter if object have large  statically declared  array or structure. No copy no problem. Most importantly the wasted cpu on moving mostly useless empty bytes is gone.  Also positive is the fact that reading and writing such a large chunk of memory  which pretty much flushed all cpu caches bringing down performance of the rest of the application is gone too.     

That's all nice and great. But chances that people will stop putting code to constructors in favor of some standard create method  are pretty slim.  

Using constructors to create everything  is paradigm that we got so used to and love. Exactly as we got trained by misleading books and used = operator for storing data to arrays.   

Still. There is way to do it with constructor via little known variant of operator new  Its called " placement new " where you construct object on existing memory with  this  pointer provided by you.  But now we are entering very weird confusing and little bit dangerous territory due to word new flying around statically declared array. New that doesn't allocate anything. New that is here just as a kludge to invoke constructor.    Why dangerous? The moment you overload something as fundamental as allocator New brace yourself for all kind of troubles  http://www.drdobbs.com/article/print?articleID=184401369&dept_url=/cpp/  

Most importantly when vector goes out of scope no destructors are automatically called. it can be done manually but it reintroduces source of bugs.

Is automatic cleanup of objects stored by pointers really that impossible in current c++?   

Now consider following weird but perfectly working example. Remember stack is defaultly limited (unless you change it in linker settings) resource.  So take this as purely academic example   that array of pointers that automatically calls destructors when going out of scope is possible.  

The moment array of objects stored by pointers goes out of scope they are automatically deallocated without any manual destructor invocation;  How come this works ? What is going on? = A() is internally the same as = new A(); the same constructor is invoked. Except for first stack is used by allocator and heap for second. both return pointers. references are pointers(just meeting certain criteria to deserve label reference) as we remember right ?   

Yes for heap pointers (created by new) there is kludge of wrapping all pointers to objects simulating pointers via operator overloading aka(smart pointers) in std::shared_ptr and alike. and store just those. So if you dont mind wrapping all your variables to functionality hiding impossible to debug macros/templates then this is very good solution for you.  

But I strongly believe that simple things shall not be encrypted or hidden from sight nor does every variable.  Programmer must be aware of what is going on as much as possible like it was in C. without having template and macro expander build in his head.  And if you ask Linus to obfuscate every pointer to template wrapper he would most probably kill you.  I remember strong backlash against excessive macro usage in C. And there was rational reason for that.    

That reason was "complexity and hiding code logic is source of bugs".      

The possibly best solution is to fix C++ compillers = operation  

So this  internally can be optimized (via explicit optimization switch) to something like 

This would fix dynamic  and static(object mem) waste = zero alloc/copy since elements are created just once in already allocated memory as it always should had been for performance reasons.  Why is static(non-dynamic) mem waste equally if not more important? Majority of objects are small and selfcontained. And when you look at benchmark bellow storing  object containing statically declared array took 5662 ms  yet storing object containing dynamic array of the same size took 1372 ms.  Also. After such change to compiler all old code using big  objects would start pretty much flying at completely different speeds just by recompiling.  

Because I am curious person I am attempting to implement and test it in clang fantastic open source c++compiler as a optimization switch or pragma. Should you wanna lend a hand I will be more than thankful http://clang-developers.42468.n3.nabble.com/Proposed-C-optimization-with-big-speed-gains-with-big-objects-tt4026886.html    But let's focus on latest C++  solution to it (unfortunately only for heap mem in your objects and with a lot of code changes)

The new C++ 11  Move semantics 

Move semantics enables you to write code that transfers dynamically allocated memory from one object to another. Move semantics works because it enables this memory to be transferred from temporary objects(by copying just pointers) that cannot be referenced elsewhere in the program. Unfortunately large statically declared  (contained-within object) arrays/structs/members data must still be uselessly copied since as mentioned they are contained within temp object themselves that is about to be destroyed.  

To implement move semantics, you typically provide a  move constructor,  and optionally a move assignment operator= to your class. Copy and assignment operations whose sources are (temp objects or data that can't change) then automatically take advantage of move semantics. Unlike the default copy constructor, the compiler does not provide a default move constructor.    You can also overload ordinary functions and operators to take advantage of move semantics. Visual C++ 2010 introduces move semantics into the Standard Template Library (STL). For example, the string class implements operations that perform move semantics. Consider the following example that concatenates several strings. 

Before && references existed, each call to operator+ allocated and returned a new temp object. operator+ couldn't append one string to the other because it didn't know whether content of the source can be tampered with (temps) or not (variables). If the source strings are both variables, they might be referenced elsewhere in the program and therefore must not be modified. But now thanks to && reference we now know that temp (which cannot be referenced elsewhere in the program) was passed in. Therefore, operator+ can now safely append one string to another. This can significantly reduce the number of dynamic memory allocations that the string class must perform. 

Move semantics also helps when the compiler cannot use Return Value Optimization (RVO) or Named Return Value Optimization (NRVO). In these cases, the compiler calls the move constructor if the type defines it.    

As an another example consider the example of inserting an element into a vector object. If the capacity of the vector object is exceeded, the vector object must reallocate memory for its elements and then copy each element to another memory location to make room for the inserted element. When an insertion operation copies an element, it creates a new element, calls the copy constructor to copy the data from the previous element to the new element, and then destroys the previous element. Move semantics enables you to move objects directly without having to perform expensive memory allocation and copy operations.

So. To take advantage of move semantics to allow efficient insert of your objects in the std::vector, you must write a move constructor to allow moving of data from one object to another. 

  • you allocate large space for building outside of city   // notice   = Skyscrapper();       
  • you build building in this space.      
  • you just mark this already created building as part of city // no trucks (copying) needed    

For complete compilable example copy benchmark code bellow to dev env of your choice. 

No for those who thing everything was clear and obvious in previous example. Dont' let the eyes fool you. 

Skyscraper && in is not actually of type && anymore. The moment it enters function its & again. So  if you wana forward && to another function you need to cast it to && again (in stl via std::move ). Why c++ decided to do this behind your back hidden functionality ? Well I am being told that it's security precaution. That any && having name is in risk of being referenced somewhere else in code and thus it's not deemed safe for keeping "moveable" status. Seems like some unfinished c++ business to me since I can't imagine referencing local variable outside of this proc. Also there is little know feature of ref-specifiers where you can restrict operator/methods to accept just temps or just variables.

Now, you can't anymore say 

Unfortunately this doesn't yet seem  to be supported in Visual Studio 2012 RC1 that I am using right now.   

So to summarize. Unless you use contained big statically declared (stored within object) structures/arrays/members the result is significant speedup of your existing code. Tho see how much you can speedup your existing code (well... actually you stop slowing it down)I created simple practical && example along with benchmark results. But if you do more than just stupid memset in your constructors/destructors speedups will be significantly higher.     

Benchmark Results:    

Store objects containing array themself took 5662 ms  // even with && this is still problem   sort   objects containing array themself took 17660 ms //this is why you should not store objects    store objects containing dynamic array by copying took 1372 ms store objects containing dynamic array by moving (c++ 11) took 500 ms store just pointers to objects took 484 ms   sort   just pointers to objects took 0 ms      , benchmark code   .

To have an idea how bad the usual careless assign = is. I created example storing 500 large objects to array via different methods and measured time it takes. Texture represents standard large object we work in c++ on daily basis = just large chunk of data and its size plus mandatory operator = to be able to be stored by value. Now it's stripped to bare minimum on purpose(no chaining consts etc) . with only simple types so you can focus only on those two operators. And sort has < reversed to simulate worst case scenario.   

Now the more observable of you would probably would start arguing...  

"This is nothing new I could do this data "moving" (or just passing data along between objects without copying) the same way in  current standard c++ operator = & so why do I need new && operator ?   

Yes you can and No you can't.    If you did moving in operator = & like this. Imagine what would happen    

If we moved = copied just pointers to data in standard operator = &  

Then whenewer b changes c changes too;  And this was not intended

so we actually wana make copy when assigning from data that can change  and we just copy pointers to data that we are sure will not change 

Unfortunately & up to c++ 11 could not distingush if passed data can change so moving was not possible in current c++ standard for the reasons explained in c=b example. the new && in turn can distingush that data which cant change was passed in and thus its safe just point to its data and skip copying. So to summarize. in new c++ 11 standard you are now supposed to keep two sets of operators and constructors 

operator = and constructor taking &   where you copy from data that can change (variables etc,)       operator = and constructor taking && where you just point to data that will not change and save mem and cpu by skipping copying  (temp objects,etc)        Unfotunately that means you will now have to implement two sets of pretty much every operator under the sun that you declared with generic copy/paste like code yet still fixing only heap side of the performance problem. So reconsider using = on objects at all. At least until compiller writers fix heap and static mem waste  by doing internal placement new for = on movable objects.  

Why it's called rvalue reference &&   , now the whole article i was deliberately was not using termins like rvalues(cant change) and lvalues(can change) since they are not what their names imply.   .

lvalue should had been named something like "variable"    rvalue should had been named something like "temp"   

They are just technical grammar remnants confusing people. the were born from how C grammar in lex and yacc was described eg on what side of "that particular grammar rule they vere located = left or right" BUT that particular rule can be part of larger expression and lvalue is sudenly rvalue. Or let me explain it this way. Anything not having name is rvalue otherwise it's lvalue 

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

assignment operator rvalue

Comments and Discussions

to use this message board.
  Layout   Per page    
First Prev
29-May-21 11:38 29-May-21 11:38 
This will not work as intended. Explanation bellow void operator = ( const string & in ) { mem = in.mem; // We move data by pointing to it size = in.size; in.mem = 0; } ... string a("white"),b("yellow"); string c=b; // c is "yellow" now ... b="gotcha..." // but c is now "gotcha..." too -> should not happen !!!

Not sure what you wanted to write, but this is just wrong. "in" is const so you cannot set in.mem = 0...

but thank for the article, helped me understanding the topic.
·  
31-Jan-21 2:29 31-Jan-21 2:29 
Destructor code such as (mem) delete mem; works but is unnecessary. It is well defined in the language specification that delete can be safely used on a nullptr.
·  
1-Aug-16 10:33 1-Aug-16 10:33 
Regarding references: "How are they made safer? Well. They can never contain invalid data since the only assignment to them is allowed during their declaration and only to existing statically declared data."
This isn't quite true. You can have a reference to dynamically-allocated data which might get de-allocated. It's possible to do something like this:
*intPtr = new int(5); int& intRef(*intPtr); delete intPtr; intPtr = nullptr; cout << "intRef = " << intRef << endl;
Typically, trying to use intRef would cause the program to crash. I just tried this with Visual Studio 2015 and it outputted 5 and did not crash, but I've seen this scenario crash with earlier versions of Visual Studio. I certainly would not rely on the compiler protecting from this type of thing.
·  
17-Apr-16 9:44 17-Apr-16 9:44 
Quoted from article: operator = ( Skyscraper && in ) { // temp obj was passed in mem = in.mem; // We copy just data pointer from temp (ie we move data) size = in.size; // which is safe since temp obj can't change in.mem = 0; // THIS IS KEY PART: we prevent deallocation when temp is destroyed } ~Skyscraper() { if(mem) delete mem; } //BUT destructor must have delete as conditional
Calling on in the destructor indicates you accept responsibility for the memory management of the memory pointed to by . If this is the case you should remember to call on before changing the value it points to like so: operator = ( Skyscraper && in){ delete mem; // ← ← ← remember to delete before you discard the old value of mem mem = in.mem; size = in.size; in.mem = 0; // should really be using nullptr instead of 0 }
As for the destructor, checking for null is , so you should just do
mem; }
·  
11-Dec-13 20:31 11-Dec-13 20:31 
But is the problem not also solved by using reference counting?

operator = (Skyscraper & in) { in.incRef(); mem = in.mem; size = in.size; } ~Skyscraper() { if(mem.decRef() <= 0) delete mem; }

In this case also one pointer is assigned to the object. It should as fast as &&. Or am I wrong?
·  
17-Oct-12 12:32 17-Oct-12 12:32 
I took your benchmark and altered it ...

Sorting an array of pointers took 0.088100 ms
Sorting an array of std::shared_ptr<> took 0.098500 ms
Sorting an array of move assignable/constructable elements took 0.122000 ms
Sorting an array of copy assignable/constructable elements took 4513.762700 ms

I got the above results using a "dumbed down" version of the comparition function that performs the compare using

When you write " ", you are forgetting a very important point: , not moving it.

The sort operation shows the value of implementing the move assignment operator and the move constructor, and while your "benchmark" shows a performance improvement, it doesn't really illustrate the real value of making your objects move assignable/constructable.

You'll find the full code here: [ ], and it does full buffer comparition, hence the difference in the timing information.

Here is the full set of results based on the "dumbed down" version of the comparition function:
elements in 1202.612700 ms sorted 500 elements in 4485.335600 ms Test vector copy: copy constructible/copy assignable copy of 500 elements in 784.731400 ms sorted 500 elements in 4563.828300 ms Test vector push_back: move constructible/move assignable push_back of 500 elements in 495.517700 ms sorted 500 elements in 0.146200 ms Test vector move constructible/move assignable copy of 500 elements in 611.347300 ms sorted 500 elements in 0.093500 ms Test vector of raw pointers push_back of 500 pointers in 510.064400 ms sorted 500 pointers in 0.137800 ms Test vector of raw pointers copy of 500 pointers in 0.002600 ms sorted 500 pointers in 0.097700 ms Test vector: push_back of shared_ptrs' push_back of 500 shared_ptrs' in 468.988400 ms sorted 500 shared_ptrs' in 0.091600 ms Test vector: Copy of shared_ptrs' copied 500 shared_ptrs' in 0.010700 ms sorted 500 shared_ptrs' in 0.098500 ms Test array of raw pointers copy of 500 pointers in 0.001900 ms sorted 500 pointers in 0.088100 ms Test array: copy constructible/copy assignable copy of 500 elements in 655.257000 ms sorted 500 elements in 4513.762700 ms Test array: Copy constructible/copy assignable (no lambda) copy of 500 elements in 618.246500 ms sorted 500 elements in 4549.465900 ms Test array: Copy of move constructible/move assignable (no lambda) copy of 500 elements in 632.684700 ms sorted 500 elements in 0.122000 ms Test array: shared_ptrs' copy of 500 shared_ptrs' in 0.010700 ms sorted 500 shared_ptrs' in 0.098500 ms

Principal Architect, Software -

·  
18-Oct-12 23:48 18-Oct-12 23:48 
Hi espen.
So you didn't like benchmark results and you made a few ... ehm...really not that important "tweaks".

heck your "tweaked" benchmark pretty much reminds me of this "tweaked" benchmark
[ ]

There the main performer (c arrays of low level pointers) kinda dissapears too


·  
19-Oct-12 2:32 19-Oct-12 2:32 
So you didn't like benchmark results and you made a few ... ehm...really not that important "tweaks".
The two buffer templates Buffer and Buffer2 both guard against self assignment, which makes their implementation a bit more robust. Buffer2 is both move constructable and move assignable - when you implement one, it's good practice to implement the other.

There the main performer (c arrays) kinda dissapears too
[ ], is mainly about the Stopwatch class, and how it can be used to provide highly accurate timing information.























So what about replacing > with - in a sort comparsion function.
That's not what I'm replacing, I'm replacing a full buffer compare
compare(const Buffer& other) const { int result = 0; size_t compareLength = min(length,other.length); for(size_t i = 0; i < compareLength; i++) { const T& v1 = ptr[i]; const T& v2 = other.ptr[i]; if(v1 > v2) { result = 1; break; } if(v1 < v2) { result = -1; break; } } if(result == 0) { if(length > other.length) { result = 1; } else if(length < other.length) { result = -1; } } return result; }
with
compare(const Buffer& other) const { return ptr[0] - other.ptr[0]; }
because that's about as computationally expensive as:
] (Texture* a, Texture* b) { return a->color > b->color; }

You write:

and fail to call attention to the obvious, which is the difference between the timings.

Let's say that 3. gives us the time required to create the 500 objects, then it took 14 ms to assign those objects to the array using move assignment versus the 888 ms (1372-484) required to perform the assignment on your copy assignable/constructable type - so implementing the move assignment operator gave you .

Now, that would rally have been something to write about. And in the case of the sort operation I'm illustrating a when it's performed on the move assignable/move constructable type using the inexpensive compare.

You're right, I intentionally left out doing something similar to what you did with the:
Texture2StaticArray : Texture2 { Texture2StaticArray() : Texture() {} Texture2StaticArray( int Size, int Color ) { memset(static_array,color=Color,sizeof(static_array)); } char static_array[640*480*8]; };
since it can't be used to illustrate the difference between implementing move assigment/construction and not.

Now, isn't a static array, just a bunch of bytes that will be part of Texture2StaticArray objects.

The declaration of a static data member in the member list of a class is not a definition. You must define the static member outside of the class declaration, in namespace scope. For example:
foo { static char a_real_static_array[640*480*8]; }; char foo::a_real_static_array[640*480*8] = {0,};
Once you define a static data member, it exists even though no objects of the static data member's class exist. In the above example, no objects of struct foo exist even though the static data member foo::a_real_static_array has been defined.


shared_ptr is bad but not That bad
I've been using smart pointers in various incarnations for nearly 20 years, and so far that seems to have been a good choice. shared_ptr has some limitations that I'm not entirely happy with - but performance is not one of them. I obviously wouldn't use std::shared_ptr<double> inside a matrix class, as it does have an overhead - but in the real world it defnitely has its place.

You're getting a vote of 1 since I find that this article is misleading and inaccurate.

You seem to take personal offence when people actually spend time and effort trying help you become a better developer, something I hope you'll stop doing.

Principal Architect, Software -

·  
17-Oct-12 5:42 17-Oct-12 5:42 
Now that's a problem I really hate! Thanks for this article.
Now I'm gonna think of way to implement this for my class Vector(x,y,z) which is used millions of times in my FPS game... Oh crap, but it's VS 6
·  
16-Oct-12 0:13 16-Oct-12 0:13 
Really interesting and useful information about c 11. And the way you explained is good.
·  
15-Oct-12 22:01 15-Oct-12 22:01 
This is a great inspiring article. I am pretty much pleased with your good work. You put really very helpful information. Keep it up once again.
·  
11-Oct-12 0:03 11-Oct-12 0:03 
I am new to c++, and trying to elimination the confusion between a pointer and reference... in the following small code snippet...
A{ int no; operator A* () { return this; } // just typecast }; void array_of_objects_stored_by_pointers_with_autocleanup() { A* a[10000]={0}; // an array of references which was not supposed to exist ? a[7] = A(); a[7]->no=7; // no "new A()". Since "A()" is the same just not on heap but on stack a[4] = A(); a[4]->no=4; // and auto cleanup of stack is build-in ;) }
as in this line comment
]={0}; // an array of references which was not supposed to exist ?
Is it not an array to A-pointers... instead array of references.
·  
12-Sep-12 1:51 12-Sep-12 1:51 
earlier, && was an extension in gcc to extract address of labels.


·  
12-Sep-12 3:20 12-Sep-12 3:20 
Well interesting to know. I guess one kludge left to allow another.

·  
11-Sep-12 12:52 11-Sep-12 12:52 
not bad, thanks
·  
11-Sep-12 10:23 11-Sep-12 10:23 
I think that this article is very bad written.
Just compare it with the VS2010 help topic "Rvalue Reference Declarator: &&."
I will appreciate you remove all repeatings, remove all skyscrapers (just C++ objects are easy understandable), make it concise, and add mentions of move semantics and move constructors.
And please make it easier to see where there are values, pointers, references, and rvalue references.
You can make it a very useful source of examples on using && rvalue references.

·  
11-Sep-12 22:36 11-Sep-12 22:36 
Anyway thx for tip. short rvalue examples that make sense are scarce. and the page you mentioned had great string example which I incorporated to article. thx for tip.
Dont' worry article is improving with every usefull tip you guys make.

·  
12-Sep-12 9:53 12-Sep-12 9:53 
Ok, by what about skyscrapers? As a reader, I found them very distractive.

·  
12-Sep-12 21:37 12-Sep-12 21:37 
Realy ? Well I wanted tho show that even in real life we dont' build and copy or dont buld and move we just build objects in place. Do you have idea about other example convincing people to change their thinking?



·  
13-Sep-12 14:20 13-Sep-12 14:20 
I do not think we have to discuss it farther, it is up to you to decide. But I think that you are writing for programmers that are using C++, and for them a fact of the performance hit in copying the large and huge objects should be clear.

·  
10-Sep-12 20:53 10-Sep-12 20:53 
Greetings,

basically, the subject is well motivated and presented. Two things I do not like:
1) The sometimes too narrative way of presentation.
2) Many of us aren't native English speakers, but there is no shame in asking friends of colleagues who speak English well to check and revise your article. The reason is that sometimes it is really unclear or even misleading what you are writing.

Otherwise, nice one!
G.


·  
10-Sep-12 20:57 10-Sep-12 20:57 
Thank you. Do you have concrete parts that should be improved?

·  
10-Sep-12 21:36 10-Sep-12 21:36 
Greetings again,

consider this part:


void operator = ( Texture & in ) { mem = in.mem; // We move data by pointing to it size = in.size; in.mem = 0; }

If you did moving in & operator like this. Imagine what would happen
Collapse | Copy Code

white"),b("yellow"); string c=b; // c is "yellow" now ... b="gotcha..." // but c is now "gotcha..." too -> should not happen !!!

if we moved = copied just pointers to data in standard operator = &

First of all, if this operator is within a class and thus overloads, I would implement it in a different way, such as

= (const classXY& src) { data = src.data return *this; }

which prevents invalidation of src in the first place.

W.r.t the language, it is just absolutely unclear and incorrect what you want to say and only looking at the code helps. This:

If you did moving in & operator like this. Imagine what would happen
Collapse | Copy Code

I interpret this as: Let us look at the disastrous effect of the following implementation when using a reference parameter incorrectly or in an unintended way.

All in all, I personally have no problems asking others for help (I am doing so right now, asking native English speakers for help with my Ph.D. thesis...)
·  
11-Sep-12 1:08 11-Sep-12 1:08 
Thx for noticing this.
The new term "moving" operator introduced in c++ 11 should be explained. I forgot about this.

Yes adding support for operetor chainig is common practice. But in this example they are useless. For beginners simple code is more clear since its visible which & the article describes.
As for const.
Well there is a lot of places where you can use const. But it often does not have much sense with move operator. with copy operators the point was not touching source and pass only copy along chain. With move operator the desire is often quite oposite. prevent useless copies by working on or just passing along the same data. you see ? const depends on application case.

I will try to explain "move" semantics more in the article.
Thx

·  
11-Sep-12 2:10 11-Sep-12 2:10 
See, this is a good example where I misunderstood your text -- I had no idea you were really referring to move semantics. Your example was not a move semantic however (no &&), but when really using move semantics I have to agree: Use && and not const&!

Best regards,
B.
·  
Last Visit: 31-Dec-99 18:00     Last Update: 26-Aug-24 0:03

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

assignment operator rvalue

21.12 — Overloading the assignment operator

  • Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers
  • Advertising & Talent Reach devs & technologists worldwide about your product, service or employer brand
  • OverflowAI GenAI features for Teams
  • OverflowAPI Train & fine-tune LLMs
  • Labs The future of collective knowledge sharing
  • About the company Visit the blog

Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Get early access and see previews of new features.

Should I use lvalue reference qualifiers for assignment operators?

Recently, I have followed a discussion about assignments to expressions in C++ as shown in the following example:

With C++11 it is possible to restrict the assignment operator to lvalue references (on the left side). When declaring the assignment operators as follow, the compiler Clang rejects the code with an error message due to incompatible types.

I haven't seen this anywhere. Is there a good reason for not using lvalue reference qualifiers for assignment operators (besides missing support in most compilers)?

  • operator-overloading
  • assignment-operator

nosid's user avatar

  • 5 One reason is that most compilers don't support the syntax yet. Another is that it doesn't solve a major problem. How often does this happen by mistake? –  Bo Persson Commented Oct 6, 2012 at 11:31
  • 1 I think it does happen. if (somefunc() = value) Of course, most compilers emit a warning for this, but not in all cases. –  Tamás Szelei Commented Feb 26, 2014 at 14:23

3 Answers 3

Interesting! I wasn't even aware of this and took me while to find it (it was part of the "Extending move semantics to *this" proposal). The notation is defined in 8.3.5 [dcl.decl] paragraph 4 in case anybody wants to have a look.

Anyway: Now, knowing about this feature it seems it is most useful to use it for overloading and possibly behave differently if the object on which a function is called is an lvalue or an rvalue. Using it to restrict what can be done, e.g., with the result of an assignment seems unnecessary, especially if the object actually happens to be an lvalue. For example, you might want the syntax to return an rvalue from assigning to an rvalue:

The intention here would be to enable moving from the assignment result (whether that is worth it, though, I'm not sure: why not skip the assignment in the first place?). I don't think that I would use this feature primarily to restrict uses.

Personally, I like the possibility to sometimes get hold of an lvalue from an rvalue and the assignment operator is often a way to do this. For example, if you need to pass an lvalue to a function but you know you don't want to use anything with it, you can use the assignment operator get hold of an lvalue:

This may be an abuse of the assignment operator to get an lvalue from an rvalue but it is unlikely to happen by accident. Thus, I wouldn't go out of my way and make this impossible by restricting the assignment operator to be applicable to lvalues only. Of course, returning an rvalue from an assignment to an rvalue would also prevent this. Which of the two uses is more useful, if any, might be a consideration.

BTW, clang seems to support the syntax you quoted since version 2.9.

Morwenn's user avatar

  • 6 I'd rather have a clear template<class T> T& as_lvalue(T&& v){ return v; } function than screwing with the minds of my fellow coders that wonder just what the heck you're trying to achieve with the assignment (which, for the move-unaware, also seems to include a useless copy of the vector ). –  Xeo Commented Oct 6, 2012 at 13:23
Is there a good reason for not using lvalue reference qualifiers for assignment operators (besides missing support in most compilers)?

No, not really. Using lvalue or rvalue qualifiers to construct a correct interface for lvalue or rvalue objects is just the same as using const , and it should be approached the same way- each function should be considered for restriction. Assignment to an rvalue doesn't really make sense, so it should be forbidden.

The reason you haven't seen it is mostly poor compiler support- rvalue refs for *this is kinda like thread_local , most compiler implementers seem to have put it near the bottom of the "Features to implement from C++11" stack.

SSJ_GZ's user avatar

  • In my view the best answer. Although I was more interested on the assignment operator, because it is a special member function. Using reference qualifiers might have some effect on the rule-of-5, inheritance, being a member variable of an aggregate or usage with standard containers and the standard library. However, I should have been asking for this information. –  nosid Commented Oct 7, 2012 at 13:21
  • 2 Be careful reading this answer. If you only read the title of the question, you might think the 'No, not really' answers the question title. It answers the question at the end of the question 'Is there a good reason for not using lvalue reference qualifiers for assignment operators?' –  mrr Commented Nov 22, 2019 at 2:16
  • Also note that the compiler support mentioned in this answer has improved considerably since it was written. Every major and almost every minor compiler supports ref-qualifiers as of 2019. –  mrr Commented Nov 22, 2019 at 2:20

One reason I'm not super enthusiast about your suggestion is that I'm trying to shy away from declaring special members altogether . Most of my assignment operators are thus implicitly declared and thus have no ref-qualifiers.

Of course, for those times when I do write a class or class template to e.g. manage ownership (see conclusion in above link), I could take care to declare those operators for lvalues only. Since it has no effects on clients however, there isn't much of a point.

Luc Danton's user avatar

  • I agree with you. If possible I avoid declaring any of the 5. However, the compiler generated assignment operator is not restricted to lvalue references. Most likely to be backward compatible. –  nosid Commented Oct 7, 2012 at 13:24

Your Answer

Reminder: Answers generated by artificial intelligence tools are not allowed on Stack Overflow. Learn more

Sign up or log in

Post as a guest.

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy .

Not the answer you're looking for? Browse other questions tagged c++ c++11 operator-overloading assignment-operator or ask your own question .

  • The Overflow Blog
  • From PHP to JavaScript to Kubernetes: how one backend engineer evolved over time
  • Featured on Meta
  • We've made changes to our Terms of Service & Privacy Policy - July 2024
  • Bringing clarity to status tag usage on meta sites
  • Feedback requested: How do you use tag hover descriptions for curating and do...
  • What does a new user need in a homepage experience on Stack Overflow?

Hot Network Questions

  • High CPU usage by process with obfuscated name on Linux server – Potential attack?
  • Implement service registry servers with load balancing strategies
  • Do mini-humans need a "real" Saturn V to reach the moon?
  • How is the grammar of this sentence explained?
  • In theory, could an object like 'Oumuamua have been captured by a three-body interaction with the sun and planets?
  • Is there anything that stops the majority shareholder(s) from destroying company value?
  • Distribute realized geometry based on field values
  • Flight left while checked in passenger queued for boarding
  • Future perfect with different modals
  • If physics can be reduced to mathematics (and thus to logic), does this mean that (physical) causation is ultimately reducible to implication?
  • Asymptotic consistency of the Clopper-Pearson interval
  • If Miles doesn’t consider Peter’s actions as hacking, then what does he think Peter is doing to the computer?
  • How to algebraically "decouple" a pair of linear equations without finding eigenvectors?
  • A simplified Blackjack C++ OOP console game
  • Hiding all UI elements within a blender window with the python API
  • Is the error in translation of Genesis 19:5 deliberate?
  • If more collisions happen with more resistance, why less heat is generated?
  • Books to read as an intro to existential philosophy
  • If the Collatz conjecture is undecidable, then it is true
  • Adding more than one image as album art to m4a via command line
  • How to apply refactoring on Latex code?
  • Picture inside the proof environment
  • Are there any rockets that leave extra fuel as a backup?
  • Is the graph of a smooth real-valued strictly monotonic function defined on a countable union of intervals of R a manifold with boundary in RXR?

assignment operator rvalue

IMAGES

  1. Assignment Operator

    assignment operator rvalue

  2. PPT

    assignment operator rvalue

  3. C++ : Is assignment operator in c++ returns rvalue or lvalue?

    assignment operator rvalue

  4. Expression || Assignment Operator || Lvalue and Rvalue In C Language

    assignment operator rvalue

  5. PPT

    assignment operator rvalue

  6. C++ : rvalue reference with assignement operator

    assignment operator rvalue

COMMENTS

  1. Move assignment operator

    The move assignment operator is called whenever it is selected by overload resolution, e.g. when an object appears on the left-hand side of an assignment expression, where the right-hand side is an rvalue of the same or implicitly convertible type.. Move assignment operators typically "steal" the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors ...

  2. Move Constructors and Move Assignment Operators (C++)

    This topic describes how to write a move constructor and a move assignment operator for a C++ class. A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying. For more information about move semantics, see Rvalue Reference Declarator: &&. This topic builds upon the following C++ class ...

  3. What are rvalues, lvalues, xvalues, glvalues, and prvalues?

    In C++03, an expression is either an rvalue or an lvalue. In C++11, an expression can be an: rvalue lvalue xvalue glvalue prvalue Two categories have become five categories. What are these new ... For example, the built-in assignment operators expect that the left operand is an lvalue and that the right operand is a prvalue and yield an lvalue ...

  4. rvalue reference with assignement operator

    It's now "obvious" that the correct way to write a copy-and-swap assignment is: T& operator=(T x) // x is a copy of the source; hard work already done. {. swap(*this, x); // trade our resources for x's. return *this; // our (old) resources get destroyed with x. } It is said that we should write assignement operator with argument by value ...

  5. Correct usage of rvalue references as parameters

    probe& operator =(probe tmp) { swap(tmp); return *this; } Now enter C++11 with rvalue references and move semantics. You decided to add a move assignment operator: probe& operator =(probe&&); Now calling the assignment operator on a temporary creates an ambiguity because both overloads are viable and none is preferred over the other.

  6. Value categories

    An rvalue can't be used as the left-hand operand of the built-in assignment or compound assignment operators. An rvalue may be used to initialize a const lvalue reference , in which case the lifetime of the temporary object identified by the rvalue is extended until the scope of the reference ends.

  7. c++

    A common use case is the "move constructor," invoked when an object is being copied from a temporary that's about to expire (a rvalue). An example is foo = bar + baz; where bar + baz is a temporary on the right-hand side of an assignment statement.. Before rvalue references, objects that made deep copies of their contents had to construct their data in one place, then copy it all to ...

  8. 12.2

    Lvalue and rvalue expressions. An lvalue (pronounced "ell-value", short for "left value" or "locator value", and sometimes written as "l-value") is an expression that evaluates to an identifiable object or function (or bit-field).. The term "identity" is used by the C++ standard, but is not well-defined. An entity (such as an object or function) that has an identity can be ...

  9. Assignment operators

    The direct assignment operator expects a modifiable lvalue as its left operand and an rvalue expression or a braced-init-list (since C++11) as its right operand, and returns an lvalue identifying the left operand after modification. The result is a bit-field if the left operand is a bit-field.

  10. Lvalue and Rvalue

    An rvalue is a temporary value that does not persist beyond the expression that uses it. [1] Discussion. Lvalue and Rvalue refer to the left and right side of the assignment operator. The Lvalue (pronounced: L value) concept refers to the requirement that the operand on the left side of the assignment operator is modifiable, usually a variable.

  11. 22.2

    22.2 — R-value references. Alex May 15, 2024. In chapter 12, we introduced the concept of value categories ( 12.2 -- Value categories (lvalues and rvalues) ), which is a property of expressions that helps determine whether an expression resolves to a value, function, or object. We also introduced l-values and r-values so that we could discuss ...

  12. Rvalue reference declarator: &&

    Holds a reference to an rvalue expression. ... To implement move semantics, you typically provide a move constructor, and optionally a move assignment operator (operator=), to your class. Copy and assignment operations whose sources are rvalues then automatically take advantage of move semantics. Unlike the default copy constructor, the ...

  13. C++ rvalue references and move semantics for beginners

    The same copy procedure occurs within the assignment operator: int main() { Holder h = createHolder(1000); // Copy constructor h = createHolder(500); // Assignment operator } The code inside our assignment operator wipes the memory out and then reallocates it from scratch by copying the data from the temporary object. Yet another two expensive ...

  14. Assignment operators

    for assignments to class type objects, the right operand could be an initializer list only when the assignment is defined by a user-defined assignment operator. removed user-defined assignment constraint. CWG 1538. C++11. E1 ={E2} was equivalent to E1 = T(E2) ( T is the type of E1 ), this introduced a C-style cast. it is equivalent to E1 = T{E2}

  15. Assignment operators

    For the built-in operator, lhs may have any non-const scalar type and rhs must be implicitly convertible to the type of lhs. The direct assignment operator expects a modifiable lvalue as its left operand and an rvalue expression or a braced-init-list (since C++11) as its right operand, and returns an lvalue identifying the left operand after modification.

  16. Understanding lvalues and rvalues in C and C++

    What happens here is that our new move assignment operator is invoked since an rvalue gets assigned to v2. The constructor and destructor calls are still needed for the temporary object that's created by Intvec(33), but another temporary inside the assignment operator is no longer needed. The operator simply switches the rvalue's internal ...

  17. The new C++ 11 rvalue reference && and why you should start using it

    rvalue should had been named something like "temp" So whenever you read text using lvalue and rvalue just replace those two and sudenly text will make sense . ... The sort operation shows the value of implementing the move assignment operator and the move constructor, and while your "benchmark" shows a performance improvement, it doesn't really ...

  18. Copy assignment operator

    Triviality of eligible copy assignment operators determines whether the class is a trivially copyable type. [] NoteIf both copy and move assignment operators are provided, overload resolution selects the move assignment if the argument is an rvalue (either a prvalue such as a nameless temporary or an xvalue such as the result of std::move), and selects the copy assignment if the argument is an ...

  19. 21.12

    21.12 — Overloading the assignment operator. Alex July 22, 2024. The copy assignment operator (operator=) is used to copy values from one object to another already existing object. As of C++11, C++ also supports "Move assignment". We discuss move assignment in lesson 22.3 -- Move constructors and move assignment .

  20. assignment operator

    Passing the rvalue rtByValue() to a function that expects an lvalue reference doesn't work because this would require the lvalue reference argument to be initialized from an rvalue. §8.5.3/5 describes how lvalue references can be initialized - I won't quote it in full, but it basically says that an lvalue reference can be initialized. either from another lvalue reference

  21. Should I use lvalue reference qualifiers for assignment operators?

    This may be an abuse of the assignment operator to get an lvalue from an rvalue but it is unlikely to happen by accident. Thus, I wouldn't go out of my way and make this impossible by restricting the assignment operator to be applicable to lvalues only. Of course, returning an rvalue from an assignment to an rvalue would also prevent this.