Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is C++ move constructor obsolete?

I wrote my own string type (Str) to demonstrate the basic constructors, destructors and assignment operators; and, I can see them all execute in C++17 except the move constructor.

Apparently, the move constructor is not used much anymore because of Return Value Optimization (RVO).

Is the move constructor only called in response to explicitly calling std::move?

When else might it be called?
Is it mostly obsolete because of RVO?

Here's my Str type:

struct Str {
  Str(): p(nullptr) {}
  Str(const char* s) { cout << "cvctor \"" << s << "\"\n"; copy(s); }
  Str(const Str& s): p(nullptr) { cout << "cpctor deep\""<<s.p<<"\"\n"; copy(s.p); }
  Str( Str&& s) { cout << "mvctr shallow \"" << s.p << "\"\n"; p = s.p; s.p=nullptr; }
  const Str& operator=(const Str& s) { cout << "op=\""<<s.p<<"\"\n"; copy(s.p); return *this; }
  const Str& operator=( Str&& s) { cout << "op= shallow \""<<s.p<<"\"\n"; p=s.p; s.p=nullptr; return *this; }
  ~Str(){ if ( p != nullptr ) { cout << "dtor \"" << p << "\"\n"; delete [] p; } }

private:
  char* p = nullptr;
  char* copy(const char* s)
};
like image 698
Siegfried Avatar asked Nov 07 '25 03:11

Siegfried


1 Answers

No not at all

Return value optimization is not the only point where the move constructor is used. Every time you want to construct a value of some type from an rvalue is a point where the move constructor is used.

You basically ask two questions in one. Let's start with

Is the move constructor only called in response to explicitly calling std::move?

The move constructor and std::move are tangentially related but in essence very separate. The move constructor is called every time you initialize a variable from an rvalue of the same type. On the other hand std::move makes it possible to explicitly get from a so-called lvalue to such an rvalue but it is not the only way.

template<typename T>
void foo(T&& value) { // A so-called universal reference
    T other_value = std::forward<T>(value);
}
foo( string{"some string"} ); // other_value is move initialized

You see, std::forward is another way to obtain an rvalue. Actually, "some string" also results in an rvalue in the above code, of type const char*.


Time for an intermezzo. If you hear rvalue you might be tempted to think about && which is an rvalue-reference. This is subtly different. The problem is that giving a name to anything makes it an lvalue. So the following code:

foo(string&& value) {
    T other_value = value;
}
foo( "some_string" ); // other_value is STILL copy initialized

foo(string&& value) {
    T other_value = std::move(value);
}
foo( "some_string" ); // other_value is now properly move initialized

The correct way to think about && is that such a reference can be initialized with an rvalue but it is itself not always such an rvalue. For more information see also here


Is it mostly obsolete because of RVO?

Two notable examples where the move constructor is prominently often used besides RVO come to mind

  • moving into method arguments

    void foo(string value);
    // Somewhere else
    string& some_string = get_me_a_string();
    foo( ::std::move(some_string) ); // Uses move constructor to initialize value
    some_string.clear(); // Most probably a no-op
                        // Doing this leaves an empty some_string
    

    Note that in the example above, the fact that some_string is a reference does not matter whether move construction is used. It is a reference to make it clear that RVO might not always be possible. In this case, after some_string has been moved from, will be left in an unspecified, but valid state which is a fancy way to say that no undefined behaviour will occur and the reference is still valid.

  • moving into fields

    class FooBar {
        string fooField;
        //Constructor
        FooBar( string bar )
        : fooField( ::std::move(bar) ) // Uses move constructor to initialize fooField
        { }
    }
    
like image 198
WorldSEnder Avatar answered Nov 08 '25 19:11

WorldSEnder



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!