Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why the push_back signature is void push_back (const value_type& val) not void push_back (value_type val)? [duplicate]

Tags:

c++

stl

Why is the function signature of push_back the following?

void push_back (const value_type& val);

The value passed is copied into the container, why not make a copy directly into the argument list?

void push_back (value_type val);
like image 664
user1438832 Avatar asked May 22 '14 06:05

user1438832


2 Answers

The answer is to avoid making yet another copy. Take a look at this simple example that illustrates the difference between using value_type and const value_type&.

#include <iostream>
using namespace std;

struct A
{
   A() {}

   A(A const& copy)
   {
      cout <<  "Came to A::A(A const& copy)\n";
   }

   void print() const
   {
      cout << "Came to A:print()\n";
   }

};

void foo(A const& a)
{
   A copy = a;
   copy.print();
}


void bar(A a)
{
   A copy = a;
   copy.print();
}

int main()
{
   A a;
   foo(a);
   bar(a);
}

Output of running the program:

Came to A::A(A const& copy)
Came to A:print()
Came to A::A(A const& copy)
Came to A::A(A const& copy)
Came to A:print()

Notice the additional call to the copy constructor due to the call to bar. For some objects, that additional copy construction and the corresponding destruction can be very expensive when the operation is carried out millions of times.

like image 56
R Sahu Avatar answered Sep 28 '22 08:09

R Sahu


Here's what an extremely simplified push_back into a vector might look like when implemented with each of those interfaces:

// by reference
void push_back (value_type const & val)
{
    // Copy val into its designated place.
    new (m_data_ptr + m_len++) value_type (val);
}

// by value
void push_back (value_type val)
{
    // Copy val into its designated place.
    // In C++11, this copy may not happen if value_type is movable. But that's
    // not always the case. (you have to use std::move too.)
    new (m_data_ptr + m_len++) value_type (val);
}

They look the same, don't they?

The problem is when you try calling them, specially the pass-by-value version:

string s;
...
v.push_back (s);

If push_back accepts its parameter by reference (i.e. value_type & val) then a reference to the existing object s is passed into the function and no copies are made here. Of course, we still need one copy inside the function, but that's kinda necessary.

However, if push_back is written to get its parameter by value (i.e. value_type val) then a copy will be made of the s string right at the call site, onto the stack and into the argument that will be named val. Here, val is not a reference to a string, it is a string and it must come from somewhere. This extra copy is what drove the designer(s) of STL and most sensible C++ libraries to adopt pass-by-reference as the preferred choice for many situations (and in case you are wondering, that const is there to tell the caller that now that this function can modify its precious object, since a reference to it is being given to the function, it won't!)

By the way, this discussion mostly applies to C++98 (i.e. the old C++.) The current C++ has Rvalue references and moving and perfect forwarding which provide for more interface options and the opportunity for cleaner and more precise and more efficient interfaces/implementations, but also make this topic a bit more complicated.

In C++11, there are two overloads of push_back (as well as a new member emplace_back) on vector and other containers.

The push_backs are:

void push_back (value_type const & val);
void push_back (value_type && val);

The second one is the correct version of what you are suggesting (i.e. it won't be ambiguous for the compiler.) It lets the implementation move the value out of that rvalue reference, and lets the compiler generate code to call the faster version if appropriate.

For backward-compatibility reasons (and probably a few other minor ones,) the old push_back signature cannot be removed from C++.

like image 42
yzt Avatar answered Sep 28 '22 07:09

yzt