Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How were move semantics addressed before C++11?

Tags:

c++

c++03

I've been reading up on move semantics lately and how it was introduced into C++11. The main gist is that programs could become more efficient by creating objects by 'stealing' pointers to temporary objects. This is much more efficient than doing a deep copy of the temporary object to create new objects.

In C++11 (and onward), this is achieved with the use of rvalue references. All temporary objects (objects that don't have a known place within your programs memory) are considered rvalues. Specifically, class constructors could now be overloaded to accept rvalue references.

So I'm curious, how was this problem of 'expensive temporary object copy' addressed prior to C++11?

like image 561
Izzo Avatar asked Dec 08 '16 04:12

Izzo


People also ask

What is a move semantic?

Move semantics is a set of semantic rules and tools of the C++ language. It was designed to move objects, whose lifetime expires, instead of copying them. The data is transferred from one object to another. In most cases, the data transfer does not move this data physically in memory.

Are move semantics important?

Move Semantics is an extremally important concept for one to understand talking about programming in c++. It is a fundamental aspect of the language that may not be that obvious and even more for one coming from another language such as Swift, C#, or Java.

What is R value Referene in C++ 11?

“l-value” refers to a memory location that identifies an object. “r-value” refers to the data value that is stored at some address in memory. References in C++ are nothing but the alternative to the already existing variable. They are declared using the '&' before the name of the variable.

Does C++ automatically move?

That's pretty much the only time you should write std::move , because C++ already uses move automatically when copying from an object it knows will never be used again, such as a temporary object or a local variable being returned or thrown from a function. That's it.


1 Answers

The main way this was done was via std::swap. std::swap could be overloaded/specialized for types where it could be executed more efficiently than the default "swap via a temporary variable" to instead perform a shallow swap.

Often data types would provide a swap() member function that could be used by this overload to access the private internals of the data type. (For example; see std::vector::swap)

For example; to "move" an element into a vector, the following code could be used:

class MyListOfVectors {
    private:
    //using `std::vector<int>` as an example of a "movable" type.
    std::vector<std::vector<int>> list;
    public:
    void emplace_back(std::vector<int> &n) {
        using std::swap;
        list.push_back(std::vector<int>());
        swap(list.back(), n);
        //(possibly add something to rollback the `push`
        // in case swap fails; to provide the strong
        // exception guarantee)
    }
};

To return an element via "move", the following code could be used:

std::vector<int> MyListOfVectors::pop_back() {
    using std::swap;
    std::vector<int> r;
    swap(list.back(), r);
    list.pop_back();
    return r; //Trust in copy elision to avoid any actual copies.
}

I don't have a reference for this, but I believe standard algorithms were allowed/encouraged to use std::swap for this purpose.

Also, if you were feeling like you wanted to do things the C++11 way, you could also use boost::move, which provides an emulation of the C++11 move semantics in C++03 (though it technically violates strict aliasing and so has undefined behavior).

like image 140
Mankarse Avatar answered Sep 29 '22 00:09

Mankarse