WalzoneInterview Prep
📞 Interviewing soon? Practice with a realistic AI mock phone interview — it calls you, then scores you. First 15 min FREE →

C++ · Advanced · question 51 of 100

What are rvalue references in C++11, and how do they enable move semantics?

📕 Buy this interview preparation book: 100 C++ questions & answers — PDF + EPUB for $5

Rvalue references are a feature introduced in C++11 that allow us to distinguish between lvalues and rvalues. They are denoted by the double ampersand (&&) and they are used to define a reference to an object that can be considered a temporary or an unnamed object. The main purpose of rvalue references is to facilitate move semantics, which is a technique for optimizing the performance of copy operations by transferring the ownership of an object’s resources instead of copying them.

An lvalue is a reference to an object that has a persistent address in memory, whereas an rvalue is an expression that does not have a persistent address in memory. For example, a variable declared with a name is an lvalue, while a literal value or the result of a function call is an rvalue. In the context of function calls, rvalues are often used as temporary objects that are created to hold intermediate results and then discarded.

Consider the following code:

    void func(int& i) {
        std::cout << "lvalue referencen";
    }
    
    void func(int&& i) {
        std::cout << "rvalue referencen";
    }
    
    int main() {
        int x = 42;
        func(x); // calls lvalue reference overload
        func(42); // calls rvalue reference overload
        return 0;
    }

In this example, we define two overloads of the func function: one that takes an lvalue reference to an int, and another that takes an rvalue reference to an int. When we call func with the variable x, the lvalue reference overload is called because x is an lvalue. However, when we call func with the literal 42, the rvalue reference overload is called because 42 is an rvalue.

Rvalue references are used extensively in move semantics to enable the efficient transfer of resources between objects. For example, consider a class MyClass that manages a dynamically allocated buffer of data:

    class MyClass {
        public:
        MyClass(size_t size) : data_(new int[size]), size_(size) {}
        ~MyClass() { delete[] data_; }
        // Copy constructor
        MyClass(const MyClass& other) : data_(new int[other.size_]), size_(other.size_) {
            std::copy(other.data_, other.data_ + other.size_, data_);
        }
        // Move constructor
        MyClass(MyClass&& other) : data_(other.data_), size_(other.size_) {
            other.data_ = nullptr;
            other.size_ = 0;
        }
        private:
        int* data_;
        size_t size_;
    };

In this example, the class MyClass defines a copy constructor and a move constructor. The copy constructor performs a deep copy of the data in the object, while the move constructor simply transfers ownership of the data from the source object to the destination object. By using an rvalue reference in the move constructor, we can efficiently move the data from one object to another without having to make a copy:

    MyClass create_object() {
        MyClass obj(1000);
        // Initialize obj
        return obj;
    }
    
    int main() {
        MyClass obj1(1000);
        MyClass obj2(std::move(obj1)); // move constructor is called
        MyClass obj3 = create_object(); // move constructor is called
        return 0;
    }

In this example, we create an object obj1 of type MyClass, and then we move its contents into a new object obj2 using std::move.

Reading is step one. Saying it out loud is the interview. Our AI interviewer calls your phone and runs a realistic C++ interview — then scores it.
📞 Practice C++ — free 15 min
📕 Buy this interview preparation book: 100 C++ questions & answers — PDF + EPUB for $5

All 100 C++ questions · All topics