From C++ Weekly Ep 404 : Code which should avoid move does a copy with std::vector
Hello Jason,
This issue refers to Ep 404 of C++ weekly series.
As suggested in the video, use of std::array's initializer list constructor does not invoke the copy / move constructor of the class. However if std::vector's initializer list constructor is used, it invokes copy constructor. I am not able to understand the reasoning behind it, and I would like you to shed some light on it.
Code:
#include <iostream>
#include <vector>
#include <array>
class A {
public:
int member;
// Parameterized constructor
A() {
std::cout << "Default constructor called" << std::endl;
}
A(int val) : member(val) {
std::cout << "Parameterized constructor called with value: " << val << std::endl;
}
// Copy constructor
A(const A& other) : member(other.member) {
std::cout << "Copy constructor called. Copied value: " << other.member << std::endl;
}
// Move constructor
A(A&& other) noexcept : member(other.member) {
other.member = 0; // Reset the moved-from object's state (optional)
std::cout << "Move constructor called. Moved value: " << member << std::endl;
}
// Destructor
~A() {
std::cout << "Destructor called. Object with value " << member << " destroyed." << std::endl;
}
// Getter method
int getMember() const {
return member;
}
};
int main() {
auto make_a = [](const int val) {
A a;
a.member = val;
return a;
};
// std::array<A, 2> arr_a{make_a(5), make_a(6)};
std::vector<A> vec_a{make_a(5), make_a(6)};
}
Compiled and executed on onlinegdb with C++14/17/20/23 with default warnings and optimizations
Thank you in Advance Jason. Your big fan!!
Nothing in the documentation of vector https://en.cppreference.com/w/cpp/container/vector/vector lead me to think it would move the elements.
Not just that, but moving the elements would be very confusing. Imagine the next code:
std::string a = "a";
std::string b = "b";
std::vector<std::string> v {a, b};
it's quite clear that a and b would be copied.
Maybe if a constructor like vector(std::initializer_list<T&&> init, const Allocator& alloc = Allocator() ); existed (but it is not part of the standard).
also you could just:
std::vector<A> vec_a;
vec_a.reserve(2);
vec_a.emplace_back(make_a(5));
vec_a.emplace_back(make_a(6));
I guess we are completely deviating from the point. 1st of all, It makes sense that a & b are copied as they are lvalues.
My question was, that in the above example, why std::array does not invoke copy / move whereas std::vector does? I don't think std::array has any sort specialized container either which enables such behavior.
The explanation is that std::array does not have any constructors. The braced initializer list syntax therefore directly initializes the array elements. By contrast, std::vector can't do that, it must take a std::initializer_list which is a read-only type - you literally cannot move from std::initializer_list elements because they are const. If you want to create a constructor capable of moving from a list of elements, use std::array instead of std::initializer_list. Since std::vector has no such constructor, your best alternative is using the iterator pair constructor with an iterator type that moves values out of the source range, for example std::move_iterator
I did a full conference talk about this issue https://www.youtube.com/watch?v=sSlmmZMFsXQ