c++ vector c++-standard-library libc++ std-bitset

¿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.