Betrachten Sie das folgende Snippet:
#include <array>
int main() {
using huge_type = std::array<char, 20*1024*1024>;
huge_type t;
}
Offensichtlich würde es auf den meisten Plattformen abstürzen, da die Standardstapelgröße normalerweise weniger als 20 MB beträgt.
Betrachten Sie nun den folgenden Code:
#include <array>
#include <vector>
int main() {
using huge_type = std::array<char, 20*1024*1024>;
std::vector<huge_type> v(1);
}
Überraschenderweise stürzt es auch ab! Der Traceback (mit einer der neuesten libstdc ++ - Versionen) führt zu einer include/bits/stl_uninitialized.h
Datei, in der die folgenden Zeilen angezeigt werden:
typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType;
std::fill(__first, __last, _ValueType());
Der Größenänderungskonstruktor vector
muss die Elemente standardmäßig initialisieren, und so wird er implementiert. Offensichtlich _ValueType()
stürzt der Stapel vorübergehend ab.
Die Frage ist, ob es sich um eine konforme Implementierung handelt. Wenn ja, bedeutet dies tatsächlich, dass die Verwendung eines Vektors großer Typen sehr begrenzt ist, nicht wahr?
std::allocator
verwendet wird.Antworten:
Es gibt keine Begrenzung für die Verwendung von automatischem Speicher durch eine Standard-API.
Sie könnten alle 12 Terabyte Stapelspeicherplatz benötigen.
Diese API erfordert jedoch nur
Cpp17DefaultInsertable
, und Ihre Implementierung erstellt eine zusätzliche Instanz über die Anforderungen des Konstruktors. Diese Implementierung sieht illegal aus, es sei denn, die Erkennung des Objekts ist trivial sicher und kopierbar.quelle
std::allocator
verwendet wird. Ich bin mir nicht sicher, warum dieser Sonderfall überhaupt gemacht wird.std::fill
für triviale Typen versenden , die dann verwendet werdenmemcpy
, um die Bytes an Stellen zu sprengen, was möglicherweise viel schneller ist als das Konstruieren vieler einzelner Objekte in einer Schleife. Ich glaube, dass die libstdc ++ - Implementierung konform ist, aber das Verursachen eines Stapelüberlaufs für große Objekte ist ein QoI-Fehler (Quality of Implementation). Ich habe es als gcc.gnu.org/PR94540 gemeldet und werde es beheben.Ich bestreite die Annahme von "am meisten". Da der Speicher des riesigen Objekts niemals verwendet wird, kann der Compiler ihn vollständig ignorieren und niemals den Speicher zuweisen. In diesem Fall würde es nicht zum Absturz kommen.
Der C ++ - Standard schränkt die Stapelverwendung nicht ein oder erkennt sogar die Existenz eines Stapels an. Also ja, es entspricht dem Standard. Man könnte dies jedoch als ein Problem der Qualität der Implementierung betrachten.
Dies scheint bei libstdc ++ der Fall zu sein. Der Absturz wurde nicht mit libc ++ (unter Verwendung von clang) reproduziert, daher scheint dies keine Einschränkung in der Sprache zu sein, sondern nur in dieser bestimmten Implementierung.
quelle
Ich bin weder ein Sprachanwalt noch ein C ++ - Standardexperte, aber cppreference.com sagt:
Vielleicht verstehe ich "Standardeinfügung" falsch, aber ich würde erwarten:
gleichwertig sein mit
Die letztere Version sollte keine Stapelkopie erstellen, sondern einen riesigen Typ direkt im dynamischen Speicher des Vektors erstellen.
Ich kann nicht verbindlich sagen, dass das, was Sie sehen, nicht konform ist, aber es ist sicherlich nicht das, was ich von einer Qualitätsimplementierung erwarten würde.
quelle
std::allocator
sollte es keinen erkennbaren Unterschied zwischen dem direkten Einfügen in den Vektorspeicher und dem Erstellen einer Zwischenkopie geben.emplace_back
aber nicht nur zum Erstellen eines Vektors. Was bedeutet, dass Sie haben können,vector<mutex> v(1)
aber nichtvector<mutex> v; v.emplace_back();
Für so etwas wie habenhuge_type
Sie möglicherweise noch eine Zuordnung und Verschiebungsoperation mehr mit der zweiten Version. Weder sollten temporäre Objekte erstellen.vector::vector(size_type, Allocator const&)
erfordert (Cpp17) DefaultInsertable