Espacios de nombres
Variantes
Acciones

Optimización de base vacía

De cppreference.com
< cpp‎ | language
 
 
 
 

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:

#include <cassert>
 
struct Base {}; // clase vacía
 
struct Derivada1 : Base 
{
    int i;
};
 
int main()
{
    // el tamaño de cualquier objeto del tipo de clase vacío es al menos 1
    assert(sizeof(Base) >= 1);
 
    // se aplica la optimización de base vacía
    assert(sizeof(Derivada1) == sizeof(int));
}

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 [[no_unique_address]]. Tomar la dirección de tal miembro resulta en una dirección que puede ser igual a la dirección de algún otro miembro del mismo objeto.

#include <cassert>
 
struct Vacia {}; // clase vacía
 
struct X 
{
    int i;
    [[no_unique_address]] Vacia v;
};
 
int main()
{
    // el tamaño de cualquier objeto de una clase vacía es al menos 1
    assert(sizeof(Vacia) >= 1);
 
    // miembro vacío se optimiza
    assert(sizeof(X) == sizeof(int));
}
(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

[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)

[editar] Enlaces externos