In C++98 the prototype for std::vector
's fill constructor has a default value for the initializer.
explicit vector (size_type n, const value_type& val = value_type(), const allocator_type& alloc = allocator_type());
C++11 uses two prototypes.
explicit vector (size_type n); vector (size_type n, const value_type& val, const allocator_type& alloc = allocator_type());
(In C++14 the fill constructor changed again, but it's not the point of this question.)
A reference link is here.
Why did C++11 deprecate the default initializer value value_type()
?
By the way, I tried to compile the following code with clang++ -std=c++11
and it issued an error, which means the value type still needs to have a default constructor like S() {}
, i.e. be default-constructible.
#include <vector> struct S { int k; S(int k) : k(k) {} // intentionally remove the synthesized default constructor }; int main() { std::vector<S> s(5); // error: no matching constructor }
The default value of a vector is 0.
The default vector constructor takes no arguments, creates a new instance of that vector. The second constructor is a default copy constructor that can be used to create a new vector that is a copy of the given vector c. All of these constructors run in linear time except the first, which runs in constant time.
In this case, all the elements are deleted, but the name of the vector is not deleted. The second way to delete a vector is just to let it go out of scope. Normally, any non-static object declared in a scope dies when it goes out of scope. This means that the object cannot be accessed in a nesting scope (block).
The C++98 took a prototype object, then copied it n times. By default the prototype was a default-constructed object.
The C++11 version constructs n default-constructed objects.
This eliminates n copies and replaces it with n default-constructions. In addition, it avoids constructing the prototype.
Suppose your class looks like this:
struct bulky { std::vector<int> v; bulky():v(1000) {} // 1000 ints bulky(bulky const&)=default; bulky& operator=(bulky const&)=default; // in C++11, avoid ever having an empty vector to maintain // invariants: bulky(bulky&& o):bulky() { std::swap(v, o.v); } bulky& operator=(bulky&& o) { std::swap(v,o.v); return *this; } };
this is a class that always owns a buffer of 1000
int
s.
if we then create a vector of bulky
:
std::vector<bulky> v(2);
in C++98 this allocated 3 times 1000 integers. In C++11 this allocated only 2 times 1000 integers.
In addition, the C++98 version requires that the type be copyable. There are non-copyable types in C++11, such as std::unique_ptr<T>
, and a vector
of default-constructed unique pointers cannot be generated using the C++98 signature. The C++11 signature has no problem with it.
std::vector<std::unique_ptr<int>> v(100);
The above wouldn't work if we still had the C++98 version.
The reason the constructor was split in two was to support "move-only" types such as unique_ptr<T>
.
This constructor:
vector(size_type n, const T& value, const Allocator& = Allocator());
requires T
to be copy constructible, because n
T
s must be copied from value
to populate the vector
.
This constructor:
explicit vector(size_type n, const Allocator& = Allocator());
does not require T
to be copy constructible, only default constructible.
The latter constructor works with unique_ptr<T>
:
std::vector<std::unique_ptr<int>> s(5);
while the former constructor does not.
Here is the proposal that made this change: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1858.html#23.2.4.1%20-%20vector%20constructors,%20copy,%20and%20assignment
And this paper has some of the rationale, though is admittedly a little on the terse side: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html
Fwiw, resize
:
void resize(size_type sz, T c = T());
was split into:
void resize(size_type sz); void resize(size_type sz, const T& c);
for the exact same reason. The first requires default constructible but not copy constructible (to support default constructible move-only types), and the second requires copy constructible.
These changes were not 100% backwards compatible. For some types (e.g. reference counted smart pointers), copy constructing from a default constructed object is not the same as default construction. However the benefit of supporting move-only types was judged to be worth the cost of this API breakage.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With