Considere el siguiente fragmento:
#include <array>
int main() {
using huge_type = std::array<char, 20*1024*1024>;
huge_type t;
}
Obviamente, se bloqueará en la mayoría de las plataformas, porque el tamaño predeterminado de la pila suele ser inferior a 20 MB.
Ahora considere el siguiente código:
#include <array>
#include <vector>
int main() {
using huge_type = std::array<char, 20*1024*1024>;
std::vector<huge_type> v(1);
}
¡Sorprendentemente también se bloquea! El rastreo (con una de las versiones recientes de libstdc ++) conduce al include/bits/stl_uninitialized.h
archivo, donde podemos ver las siguientes líneas:
typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType;
std::fill(__first, __last, _ValueType());
El cambio de tamaño vector
constructor de debe inicializar por defecto los elementos, y así es como se implementa. Obviamente, _ValueType()
temporal bloquea la pila.
La pregunta es si se trata de una implementación conforme. En caso afirmativo, en realidad significa que el uso de un vector de tipos enormes es bastante limitado, ¿no es así?
std::allocator
se usa el predeterminado .Respuestas:
No hay límite en cuanto al almacenamiento automático que utiliza cualquier API estándar.
Todos podrían requerir 12 terabytes de espacio de pila.
Sin embargo, esa API solo requiere
Cpp17DefaultInsertable
, y su implementación crea una instancia adicional sobre lo que requiere el constructor. A menos que esté cerrado detrás de detectar que el objeto es trivialmente copiable y copiable, esa implementación parece ilegal.fuente
std::allocator
se usa el predeterminado . No estoy seguro de por qué este caso especial se hace en primer lugar.std::fill
tipos triviales, que luego se utilizanmemcpy
para disparar los bytes en lugares, lo que es potencialmente mucho más rápido que construir muchos objetos individuales en un bucle. Creo que la implementación de libstdc ++ se está conformando, pero causar un desbordamiento de la pila para objetos grandes es un error de calidad de implementación (QoI). Lo informé como gcc.gnu.org/PR94540 y lo solucionaré.Disputo la suposición de "la mayoría". Dado que la memoria del gran objeto nunca se usa, el compilador puede ignorarlo por completo y nunca asignar la memoria, en cuyo caso no habría bloqueo.
El estándar C ++ no limita el uso de la pila, ni siquiera reconoce la existencia de una pila. Entonces, sí, se ajusta al estándar. Pero se podría considerar que se trata de un problema de calidad de implementación.
Ese parece ser el caso con libstdc ++. El bloqueo no se reprodujo con libc ++ (usando clang), por lo que parece que esto no es una limitación en el lenguaje, sino solo en esa implementación particular.
fuente
No soy un abogado de idiomas ni un experto en estándares de C ++, pero cppreference.com dice:
Quizás estoy malinterpretando "insertado por defecto", pero esperaría:
ser equivalente a
La última versión no debería crear una copia de la pila, sino construir un enorme_tipo directamente en la memoria dinámica del vector.
No puedo decir con autoridad que lo que está viendo no cumple, pero ciertamente no es lo que esperaría de una implementación de calidad.
fuente
std::allocator
, por lo tanto, no debería haber una diferencia observable entre insertar directamente en la memoria de vectores y crear una copia intermedia.emplace_back
pero no solo para crear un vector. Lo que significa que puede tener,vector<mutex> v(1)
pero novector<mutex> v; v.emplace_back();
para algo comohuge_type
todavía podría tener una asignación y mover la operación más con la segunda versión. Ninguno de los dos debería crear objetos temporales.vector::vector(size_type, Allocator const&)
requiere (Cpp17) DefaultInsertable