rvalue


Concept: rvalues

An rvalue in C++ is a temporary object or a value that does not have a persistent memory location. Unlike lvalues, rvalues typically appear on the right-hand side of an assignment and are often created during expressions or function calls. Rvalues are not addressable, meaning you cannot take their address with the & operator, and they are often used as inputs to functions or operations.

Key Characteristics of Rvalues

Example 1: Simple Rvalues

#include <iostream>

int main() {
    int a = 10;        // 'a' is an lvalue, '10' is an rvalue
    int b = a + 5;     // 'a + 5' is an rvalue

    std::cout << "b: " << b << std::endl;

    return 0;
}

Explanation:

10: This is an rvalue because it is a literal that does not refer to any persistent memory location. a + 5: This expression is an rvalue because it results in a temporary value that is not stored in a specific memory location. It is used to initialize b, which is an lvalue.

Example 2: Rvalue References

C++11 introduced rvalue references (denoted by T&&), which allow you to bind to rvalues. Rvalue references are essential for implementing move semantics, which enable the efficient transfer of resources from temporary objects.

#include <iostream>

void process(int&& x) {
    std::cout << "Rvalue reference: " << x << std::endl;
}

int main() {
    process(10);       // 10 is an rvalue, bound to int&&

    int a = 20;
    // process(a);     // Error: 'a' is an lvalue, cannot bind to int&&

    process(std::move(a));  // std::move(a) casts 'a' to an rvalue

    return 0;
}

Explanation:

Rvalues and Move Semantics

Move semantics leverage rvalues to efficiently transfer resources from temporary objects, avoiding the overhead of copying.

Example 3: Move Constructor and Rvalues

#include <iostream>
#include <string>

class MyClass {
public:
    std::string data;

    // Constructor
    MyClass(const std::string& str) : data(str) {
        std::cout << "Constructed\n";
    }

    // Move Constructor
    MyClass(MyClass&& other) noexcept : data(std::move(other.data)) {
        std::cout << "Move Constructed\n";
    }
};

int main() {
    MyClass obj1("Hello, World!");
    MyClass obj2(std::move(obj1));  // obj1 is cast to an rvalue using std::move

    std::cout << "obj2.data: " << obj2.data << std::endl;

    return 0;
}

Explanation:

Example 4: Rvalues from Expressions

Rvalues often arise from expressions or temporary objects, such as the return value of a function or the result of an arithmetic operation.

#include <iostream>

int add(int x, int y) {
    return x + y;  // The result of 'x + y' is an rvalue
}

int main() {
    int result = add(5, 10);  // 'add(5, 10)' is an rvalue

    std::cout << "result: " << result << std::endl;

    return 0;
}

Explanation:

Example 5: Rvalues and const

You can bind rvalues to const lvalue references, which allows you to pass temporary values to functions without modifying them.

#include <iostream>

void print(const int& x) {
    std::cout << "Value: " << x << std::endl;
}

int main() {
    print(10);  // 10 is an rvalue, but can be bound to 'const int&'

    return 0;
}

Explanation:

Difference Between Lvalues and Rvalues

Lvalues:

Rvalues:

Example 6: Lvalues vs. Rvalues

#include <iostream>

int main() {
    int x = 10;         // 'x' is an lvalue
    int y = x + 5;      // 'x + 5' is an rvalue

    int* p1 = &x;       // OK: 'x' is an lvalue
    // int* p2 = &(x + 5); // Error: 'x + 5' is an rvalue, can't take its address

    y = 15;             // OK: 'y' is an lvalue

    return 0;
}

Explanation:

Summary

Understanding rvalues and their role in move semantics is fundamental to writing efficient and modern C++ code. Rvalues allow you to take advantage of temporary objects without incurring the cost of unnecessary copying.

Previous Page | Course Schedule | Course Content