En C++ 98, le prototype du constructeur de remplissage de std::vector
A une valeur par défaut pour l'initialiseur.
explicit vector (size_type n, const value_type& val = value_type(),
const allocator_type& alloc = allocator_type());
C++ 11 utilise deux prototypes.
explicit vector (size_type n);
vector (size_type n, const value_type& val,
const allocator_type& alloc = allocator_type());
(En C++ 14, le constructeur de remplissage a changé à nouveau, mais ce n'est pas le but de cette question.)
Un lien de référence est ici .
Pourquoi C++ 11 a-t-il déprécié la valeur d'initialisation par défaut value_type()
?
Par ailleurs, j'ai essayé de compiler le code suivant avec clang++ -std=c++11
Et il a généré une erreur, ce qui signifie que le type de valeur doit toujours avoir un constructeur par défaut comme S() {}
, c'est-à-dire être constructible par défaut .
#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
}
Le C++ 98 a pris un objet prototype, puis l'a copié n fois. Par défaut, le prototype était un objet construit par défaut.
La version C++ 11 construit n objets construits par défaut.
Cela élimine n copies et le remplace par n constructions par défaut. De plus, cela évite de construire le prototype.
Supposons que votre classe ressemble à ceci:
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;
}
};
c'est une classe qui toujours possède un tampon de 1000
int
s.
si nous créons alors un vecteur de bulky
:
std::vector<bulky> v(2);
en C++ 98, cela a alloué 3 fois 1000 entiers. En C++ 11, cela n'allouait que 2 fois 1 000 entiers.
De plus, la version C++ 98 requiert que le type soit copiable. Il existe des types non copiables en C++ 11, tels que std::unique_ptr<T>
et un vector
de pointeurs uniques construits par défaut ne peuvent pas être générés à l'aide de la signature C++ 98. La signature C++ 11 n'a aucun problème avec elle.
std::vector<std::unique_ptr<int>> v(100);
Ce qui précède ne fonctionnerait pas si nous avions toujours la version C++ 98.
La raison pour laquelle le constructeur a été divisé en deux était de prendre en charge les types de déplacement uniquement tels que unique_ptr<T>
.
Ce constructeur:
vector(size_type n, const T& value, const Allocator& = Allocator());
nécessite que T
soit constructible, car n
T
s doit être copié à partir de value
pour remplir le vector
.
Ce constructeur:
explicit vector(size_type n, const Allocator& = Allocator());
ne pas nécessite que T
soit constructible par copie, uniquement constructible par défaut.
Ce dernier constructeur fonctionne avec unique_ptr<T>
:
std::vector<std::unique_ptr<int>> s(5);
contrairement à l'ancien constructeur.
Voici la proposition qui a apporté cette modification: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1858.html#23.2.4.1%20-%20vector% 20constructors,% 20copy,% 20and% 20assignment
Et cet article a une partie de la justification, bien qu'il soit certes un peu laconique: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html
Fwiw, resize
:
void resize(size_type sz, T c = T());
a été divisé en:
void resize(size_type sz);
void resize(size_type sz, const T& c);
pour exactement la même raison. Le premier nécessite un constructible par défaut mais pas un constructible par copie (pour prendre en charge les types constructibles par défaut constructibles uniquement), et le second nécessite un constructible par copie.
Ces modifications n'étaient pas 100% rétrocompatibles. Pour certains types (par exemple, les pointeurs intelligents comptés par référence), la construction de copie à partir d'un objet construit par défaut n'est pas la même que la construction par défaut. Cependant, l'avantage de la prise en charge des types de déplacement uniquement a été jugé valoir le coût de cette rupture de l'API.