jeudi 20 avril 2017

Confused by how the rvalue reference vector::push_back function increases effciency

From the cppreference.com, I found a simple example about using the std::move:

std::string str = "Hello";
std::vector<std::string> v;

// uses the push_back(const T&) overload, which means 
// we'll incur the cost of copying str
v.push_back(str);                                    // First push_back
std::cout << "After copy, str is \"" << str << "\"\n";

// uses the rvalue reference push_back(T&&) overload, 
// which means no strings will be copied; instead, the contents
// of str will be moved into the vector.  This is less
// expensive, but also means str might now be empty.
v.push_back(std::move(str));                        // Second push_back

The comment says that string copy was avoided.

The first push_back will call: void push_back(const value_type& _Val)

The second push_back will call: void push_back(value_type&& _Val)

I checked the implementation code of the two functions:

void push_back(const value_type& _Val)
    {   // insert element at end
    if (_Inside(_STD addressof(_Val)))
        {   // push back an element
        size_type _Idx = _STD addressof(_Val) - _Unfancy(this->_Myfirst());
        if (this->_Mylast() == this->_Myend())
            _Reserve(1);
        _Orphan_range(this->_Mylast(), this->_Mylast());
        this->_Getal().construct(_Unfancy(this->_Mylast()),
            this->_Myfirst()[_Idx]);
        ++this->_Mylast();
        }
    else
        {   // push back a non-element
        if (this->_Mylast() == this->_Myend())
            _Reserve(1);
        _Orphan_range(this->_Mylast(), this->_Mylast());
        this->_Getal().construct(_Unfancy(this->_Mylast()),
            _Val);
        ++this->_Mylast();
        }
    }

and

void push_back(value_type&& _Val)
    {   // insert by moving into element at end
    if (_Inside(_STD addressof(_Val)))
        {   // push back an element
        size_type _Idx = _STD addressof(_Val) - _Unfancy(this->_Myfirst());
        if (this->_Mylast() == this->_Myend())
            _Reserve(1);
        _Orphan_range(this->_Mylast(), this->_Mylast());
        this->_Getal().construct(_Unfancy(this->_Mylast()),
            _STD forward<value_type>(this->_Myfirst()[_Idx]));
        ++this->_Mylast();
        }
    else
        {   // push back a non-element
        if (this->_Mylast() == this->_Myend())
            _Reserve(1);
        _Orphan_range(this->_Mylast(), this->_Mylast());
        this->_Getal().construct(_Unfancy(this->_Mylast()),
            _STD forward<value_type>(_Val));
        ++this->_Mylast();
        }
    }

So, according to my understanding, both first push_back (v.push_back(str);) and second push_back (v.push_back(std::move(str));) will trigger the vector to construct a std::string type variable and attach it to the vector.

So, actually in both push_back calls, string was not copied. And, for both push_back, the expense are the same, because both calls basically do the same thing except the second push_back will make the str input to be empty.

As for the efficiency, the only difference I can think of is that the second push_back will not call the delete []char; in the destructor of std::string which makes the second push_back call a little bit more efficient.

Not sure if my understanding is correct. Thanks a lot!

Aucun commentaire:

Enregistrer un commentaire