Skip to content

Move Semantics

Eric Brown edited this page Feb 14, 2025 · 5 revisions

Move Semantics

  • Transferring the pointer to the underlying memory instead of making a copy (memcpy)
  • Situations where we do not want to copy objects around but to directly move it for better performance
  • C++11 introduced rvalue references which are necessary for move semantics

Copy constructor

  • This is a faux String class to give an example of a String copy constructor
String(const String& other) {
    printf("Copied");
    m_Size = other.m_Size;
    m_Data = new char[m_Size]; // must allocate new memory for the copy
    memcpy(m_Data, other.m_data, m_Size);
}
  • This copy constructor is beneficial when the copy is coming from an lvalue
  • However this is a waste of time and memory if it is provided an rvalue

Move constructor

  • This is a faux String class to give an example of a String move constructor
String(String&& other) {
    printf("Moved");
    m_Size = other.m_Size;
    m_Data = other.m_Data; // have the new m_Data instance take the pointer to the string data

    // when other is deleted, it will free the heap data both the pointer both self and other.m_Data point to
    // other.m_Data has to point to something else
    other.m_Size = 0;
    other.m_Data = nullptr;
}

lvalues and rvalues

This wiki will go over basics of C++ lvalues and rvalues

  • lvalues are values with a name and address in memory

    • lvalue references can only take lvalues in unless they are const
  • rvalues are temporary values

    • rvalue references can only take temporary rvalues
  • lvalues typically called locator values as they have some sort of location

  • Cannot take an lvalue reference from an rvalue

Code Examples

1

int i = 10;
  • Think of this expression as two parts, the left and right side
    • Typically, the lvalue is on the left side of the equals sign
    • And typically, the rvalue is on the right side

Note: the "sides" do not always apply

  • In this example
    • Variable i is the lvalue; it is a variable with a location in memory
    • Then 10 is the rvalue; it is a numeric literal with no storage or location
      • Until it is assigned to an lvalue
      • Cannot assign values to rvalues because it has no location

2

int getValue() {
    return 10;
}

int main() {
    int i = getValue();
}
  • In this case, getValue() returns an rvalue; a temporary value
    • It is temporary because the returned int has no location or storage
  • The rvalue is being stored into the lvalue

3

// returns an lvalue reference
int& getValue() {
    static int value = 10;
    return value ;
}

int main() {
    int i = getValue();
    getValue() = i;
}
  • getValue() has been modified to hold a static int
    • Therefore it can now be used as an lvalue

4

// returns an lvalue reference
void setValue(const int& value) {
    // do something with value
}

int main() {
    int i = 10;
    setValue(i);  // not temporary
    setValue(10); // temporary
}
  • Cannot take an lvalue reference from an rvalue
    • if setValue(int& value) params takes a reference to an lvalue, it cannot therefore take an rvalue, i.e. 10
  • If it is a const lvalue reference, it can be assigned an rvalue:
int i = 10;
int& a = 10;       // THIS DOES NOT WORK
cosnt int& a = 10; // THIS WORKS

Expression Value Categories

C++17 updated the lvalue and rvalue to have more specific categories

image

  • glvalue ("generalized" lvalue): is an expression whose evaluation determines the identity of an object or function
  • prvalue ("pure" rvalue): is an expression whose evaluation
    • computes the value of an operand of a built-in operator (such prvalue has no result object)
    • initializes an object (such prvalue has a result object)
  • xvalue ("eXpiring" value): a glvalue that denotes an object whose resources can be reused
  • lvalue: a glvalue that is not an rvalue
  • rvalue: a prvalue or xvalue -prvalues

Clone this wiki locally