¿Por qué el vector de libc++<bool>:: const_reference no es bool?
c++-standard-library std-bitset (1)
La sección 23.3.7
vector<bool>
clase
vector<bool>
[vector.bool], párrafo 1 establece:
template <class Allocator> class vector<bool, Allocator> {
public:
// types:
typedef bool const_reference;
...
Sin embargo, este programa no se compila cuando se usa libc ++:
#include <vector>
#include <type_traits>
int
main()
{
static_assert(std::is_same<std::vector<bool>::const_reference, bool>{}, "?");
}
Además, noto que el estándar C ++ ha sido consistente en esta especificación desde C ++ 98. Además, observo que libc ++ no ha seguido esta especificación desde la primera introducción de libc ++.
¿Cuál es la motivación para esta no conformidad?
La motivación para esta extensión, que es detectable por un programa conforme, y por lo tanto no conforme, es hacer que el
vector<bool>
comporte más como el
vector<char>
con respecto a las referencias (const y de otro modo).
Introducción
Desde 1998, el
vector<bool>
ha sido ridiculizado como "no del todo un contenedor".
LWG 96
, uno de los primeros temas de LWG, lanzó el debate.
Hoy, 17 años después, el
vector<bool>
permanece prácticamente sin cambios.
Este artículo presenta
algunos ejemplos específicos sobre cómo el comportamiento del
vector<bool>
difiere de cualquier otra instanciación del
vector
, dañando así el código genérico.
Sin embargo, el mismo documento analiza en detalle las muy buenas propiedades de rendimiento que el
vector<bool>
puede tener si se implementa correctamente.
Resumen
:
vector<bool>
no es un contenedor incorrecto.
En realidad es bastante útil.
Simplemente tiene un mal nombre.
Volver a
const_reference
Como se presentó anteriormente, y se
detalla aquí
, lo malo del
vector<bool>
es que se comporta de manera diferente en código genérico que otras instancias de
vector
.
Aquí hay un ejemplo concreto:
#include <cassert>
#include <vector>
template <class T>
void
test(std::vector<T>& v)
{
using const_ref = typename std::vector<T>::const_reference;
const std::vector<T>& cv = v;
const_ref cr = cv[0];
assert(cr == cv[0]);
v[0] = 1;
assert(true == cv[0]);
assert(cr == cv[0]); // Fires!
}
int
main()
{
std::vector<char> vc(1);
test(vc);
std::vector<bool> vb(1);
test(vb);
}
La especificación estándar dice que la afirmación marcada
// Fires!
se disparará, pero solo cuando la
test
se ejecute con un
vector<bool>
.
Cuando se ejecuta con un
vector<char>
(o cualquier
vector
además de
bool
cuando se asigna un
T
no predeterminado apropiado), la prueba pasa.
La implementación de libc ++ buscaba minimizar los efectos negativos de que el
vector<bool>
comportara de manera diferente en el código genérico.
Una cosa que hizo para lograr esto es hacer que el
vector<T>::const_reference
una
referencia de proxy
, al igual que el
vector<T>::reference
especificado
vector<T>::reference
, excepto que no se puede asignar a través de él.
Es decir, en libc ++, el
vector<T>::const_reference
es esencialmente un puntero al bit dentro del
vector
, en lugar de una copia de ese bit.
En libc ++, la
test
anterior pasa tanto para el
vector<char>
como para el
vector<bool>
.
¿A que costo?
La desventaja es que esta extensión es detectable, como se muestra en la pregunta. Sin embargo, muy pocos programas realmente se preocupan por el tipo exacto de este alias, y más programas se preocupan por el comportamiento.
¿Cuál es la motivación para esta no conformidad?
Para dar al cliente libc ++ un mejor comportamiento en código genérico, y quizás después de suficientes pruebas de campo, proponga esta extensión a un futuro estándar de C ++ para el mejoramiento de toda la industria de C ++.
Tal propuesta podría venir en forma de un nuevo contenedor (por ejemplo,
bit_vector
) que tiene la misma API que el
vector<bool>
, pero con algunas actualizaciones como la
const_reference
analiza aquí.
Seguido de desaprobación (y eventual eliminación) de la especialización del
vector<bool>
.
bitset
también podría usar un poco de actualización en este departamento, por ejemplo, agregar
const_reference
y un conjunto de iteradores.
Es decir, en retrospectiva, el conjunto de
bitset
es el
vector<bool>
(que debe renombrarse a
bit_vector
, o lo que sea), como la
array
es el
vector
.
Y la analogía debería ser cierta tanto si estamos hablando de
bool
como el
value_type
de
vector
y
array
.
Hay varios ejemplos de características de C ++ 11 y C ++ 14 que comenzaron como extensiones en libc ++. Así es como evolucionan los estándares. La experiencia de campo positiva demostrada real tiene una fuerte influencia. La gente de los estándares es un grupo conservador cuando se trata de cambiar las especificaciones existentes (como deberían ser). Adivinar, incluso cuando está seguro de que está adivinando correctamente, es una estrategia arriesgada para desarrollar un estándar reconocido internacionalmente.