Optimización de base vacía
Permite que el tamaño de un subobjeto base vacío sea cero.
Contenido |
[editar] Explicación
El tamaño de cualquier objeto o subobjeto miembro (a menos que se use el atributo [[no_unique_address]] -- véase abajo) (desde C++20) se requiere que sea al menos 1, incluso si el tipo es un tipo de clase vacío (es decir, una clase o estructura que no tiene datos miembro no estáticos), para poder garantizar que las direcciones de distintos objetos del mismo tipo siempre sean distintas.
Sin embargo, los subobjetos de la clase base no están constreñidos a esto, y pueden optimizarse completamente desde el diseño del objeto:
Se prohíbe la optimización de base vacía si una de las clases base vacías es también el tipo o la base del tipo del primer dato miembro no estático, ya que se requiere que los dos subobjetos base del mismo tipo tengan direcciones diferentes dentro de la representación del objeto del tipo más derivado.
Un ejemplo típico de tal situación es la implementación sencilla de std::reverse_iterator (derivado de la base vacía std::iterator), que mantiene el iterador subyacente (también derivado de std::iterator) como su primer dato miembro no estático.
#include <cassert> struct Base {}; // clase vacía struct Derivada1 : Base { int i; }; struct Derivada2 : Base { Base c; // Base, ocupa 1 byte, seguido del relleno para i int i; }; struct Derivada3 : Base { Derivada1 c; // derivada de Base, ocupa sizeof(int) bytes int i; }; int main() { // optimización de base vacía no se aplica, // la base ocupa 1 byte, el miembro Base ocupa 1 byte // seguido de 2 bytes de relleno // para satisfacer los requerimientos de alineación de int assert(sizeof(Derivada2) == 2*sizeof(int)); // optimización de base vacía no se aplica, // la base toma al menos 1 byte más el relleno // para satisfacer el requerimiento de alineación del primer miembro // (cuya alineación es la misma que int) assert(sizeof(Derivada3) == 3*sizeof(int)); }
Se requiere la optimización de base vacía para StandardLayoutTypes para poder mantener el requerimiento de que el puntero a un objeto con diseño estándar, convertido usando reinterpret_cast, apunte a su miembro inicial, que es la razón por la cual los requerimientos de un tipo con diseño estándar incluyen "tiene todos los miembros no estáticos declarados en la misma clase (ya sea todos en la derivada o todos en alguna clase base)", y "no tiene clases base del mismo tipo como el primer dato miembro no estático". |
(desde C++11) |
Se permite que los subobjetos miembro vacíos se optimicen al igual que las clases base vacías si usan el atributo |
(desde C++20) |
[editar] Notas
La optimización de base vacía se usa comúnmente por las clases de la biblioteca estándar conscientes de asignadores (std::vector, std::function, std::shared_ptr, etc.) para evitar ocupar almacenamiento adicional para el miembro asignador si el asignador no mantiene estado. Esto se consigue almacenando uno de los datos miembro requeridos (p. ej., begin
, end
, o un puntero de capacidad para el vector) en una manera equivalente a boost::compressed_pair
con el asignador.
[editar] Véase también
- Atributo no_unique_address
[editar] Referencias
- El estándar C++20 (ISO/IEC 14882:2020):
- 7.6.10 Equality operators [expr.eq]
- 7.6.2.4 Sizeof [expr.sizeof]
- 11 Classes [class]
- 11.4 Class members [class.mem]
- El estándar C++17 (ISO/IEC 14882:2017):
- 8.10 Equality operators [expr.eq]
- 8.3.3 Sizeof [expr.sizeof]
- 12 Classes [class]
- 12.2 Class members [class.mem]
- El estándar C++14 (ISO/IEC 14882:2014):
- 5.10 Equality operators [expr.eq]
- 5.3.3 Sizeof [expr.sizeof]
- 9 Classes [class]
- 9.2 Class members [class.mem]
- El estándar C++11 (ISO/IEC 14882:2011):
- 5.10 Equality operators [expr.eq](p: 2)
- 5.3.3 Sizeof [expr.sizeof](p: 2)
- 9 Classes [class](p: 4,7)
- 9.2 Class members [class.mem](p: 20)
- El estándar C++98 (ISO/IEC 14882:1998):
- 5.10 Equality operators [expr.eq](p: 2)
- 5.3.3 Sizeof [expr.sizeof](p: 2)
- 9 Classes [class](p: 3)