Why does the inner elements of the vector are copied when the vector is passed by value?
#include<vector>
using namespace std;
// this func won't modify v[2].
// Meaning v[2] (and the whole inner array) was copied
// when v is passed to the func?
void modify(vector<int> v) {
v[2] = 100;
}
// this func modify v[2]
void modify(vector<int>& v) {
v[2] = 100;
}
int main() {
vector<int> v = {1,2,3,4,5,6};
// still same
modify(v);
// modified
modified2(v);
}
I find that it's strange that the actual content of the vector is copied when the vector is passed by value. I picture that the std::vector implementation must have a pointer field which maps to an address on heap, where the actual array is located. So when the vector is passed, even by value, the address should stay the same, pointing to the same content. Something like this:
#include<iostream>
using namespace std;
// a dummy wrapper of an array
// trying to mock vector<int>
class vector_int {
public:
int* inner_array; // the actual array
vector_int(int *a) {
inner_array = a;
}
int* at(int pos) {
return inner_array+pos;
}
};
// this passes the "mocked vector" by value
// but 'inner_array' is not copied
void modify(vector_int v) {
*(v.at(2)) = 10;
}
int main() {
int* a = new int[3] {1,2,3};
vector_int v = vector_int(a);
modify(v); // v[2] is modified
}
Is this assumption about the std::vector implementation correct? What makes the vector content being copied when passed by value?
EDIT
Thanks to alter igel's answer and UnholySheep's comment, I figured out the reason why std::vector has value sementics (or why the inner array got copied).
If the copy constructor class is defined explicitly in the class definition, the copy constructor will determine how the struct/class instance is copied when the variable is passed in a function call. So I can define a copy constructor to my vector_int, in which I copy the whole inner_array, like
#include<iostream>
using namespace std;
class vector_int {
public:
int* inner_array;
int len;
vector_int(int *a, int len) {
inner_array = a;
this->len = len;
}
int* at(int pos) {
return inner_array+pos;
}
// this is the copy constructor
vector_int(const vector_int &v2) {
inner_array = new int;
for (int i =0; i < v2.len; i++) {
*(inner_array+i) = *(v2.inner_array+i);
}
}
};
// Yay, the vector_int's inner_array is copied
// when this function is called
// and no modification of the original vector is done
void modify(vector_int v) {
*(v.at(2)) = 10;
}
int main() {
int* a = new int[3] {1,2,3};
vector_int v = vector_int(a,3);
//
modify(v);
}
I checked the source code of the stdlib implementation on my local computer (g++ Apple LLVM version 10.0.0). The std::vector defines a copy constructor which looks like this
template <class _Tp, class _Allocator>
vector<_Tp, _Allocator>::vector(const vector& __x)
: __base(__alloc_traits::select_on_container_copy_construction(__x.__alloc()))
{
size_type __n = __x.size();
if (__n > 0)
{
allocate(__n);
__construct_at_end(__x.__begin_, __x.__end_, __n);
}
}
which looks like it does an malloc for the actual copied array + copy the array.
std::vector<T>and not copyT's in turn use aconstreference:void foo(const std::vector<T>& v);- πάντα ῥεῖstd::vectorto have value semantics, copying it implies copying it's elements. There exists container implementations which perform copy-on-write but this is not practical forstd::vector. And this approach loses a lot of it's benefits since the introduction of move semantics. If you are concerned about the cost of copying your vector then you likely don't mean to copy it and should read about reference types and move semantics instead. - François Andrieux