Howard Hinant
Sección 23.3.7 Clase vector<bool>
[vector.bool]el párrafo 1 establece:
template <class Allocator> class vector<bool, Allocator> {
public:
// types:
typedef bool const_reference;
...
Sin embargo, este programa falla al compilar cuando se usa libc++:
#include <vector>
#include <type_traits>
int
main()
{
static_assert(std::is_same<std::vector<bool>::const_reference, bool>{}, "?");
}
Además, observo que el estándar C++ ha sido consistente en esta especificación desde C++98. Y además noto que libc++ no ha seguido esta especificación de manera consistente desde la primera introducción de libc++.
¿Cuál es la motivación de esta no conformidad?
Howard Hinant
La motivación para esta extensión, que es detectable por un programa conforme, y por lo tanto no conforme, es hacer vector<bool>
comportarse más como vector<char>
con respecto a las referencias (const y otras).
Introducción
Desde 1998, vector<bool>
ha sido ridiculizado como “no del todo un contenedor”. LWG 96, uno de los primeros temas de LWG, abrió el debate. Hoy, 17 años después, vector<bool>
permanece prácticamente sin cambios.
Este papel entra en algunos ejemplos específicos sobre cómo el comportamiento de vector<bool>
difiere de cualquier otra instanciación de vector
, dañando así el código genérico. Sin embargo, el mismo documento analiza extensamente las muy buenas propiedades de rendimiento. vector<bool>
puede tener si se implementa correctamente.
Resumen: vector<bool>
no es un mal contenedor. En realidad es bastante útil. Simplemente tiene un mal nombre.
De regreso const_reference
Como se introdujo anteriormente, y detallado aquíque tiene de malo vector<bool>
es que se comporta de manera diferente en código genérico que en otros vector
instanciaciones He aquí 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 sólo cuando test
se ejecuta con un vector<bool>
. Cuando se ejecuta con un vector<char>
(o cualquier vector
además bool
cuando un no predeterminado apropiado T
está asignado), la prueba pasa.
La implementación de libc++ buscó minimizar los efectos negativos de tener vector<bool>
se comportan de manera diferente en el código genérico. Una cosa que hizo para lograr esto es hacer vector<T>::const_reference
a referencia de proxyal igual que el especificado vector<T>::reference
, excepto que no puede asignar a través de él. Es decir, en libc++, vector<T>::const_reference
es esencialmente un puntero al bit dentro del vector
en lugar de una copia de ese bit.
En libc++ lo anterior test
pases para los dos vector<char>
y 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 de esta no conformidad?
Para brindarle al cliente libc++ un mejor comportamiento en el 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 mejorar 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 la actual vector<bool>
pero con algunas mejoras como el const_reference
discutido aquí. Seguido por la desaprobación (y eventual eliminación) del vector<bool>
especialización. bitset
también le vendría bien una pequeña mejora en este departamento, por ejemplo, añadir const_reference
y un conjunto de iteradores.
Es decir, en retrospectiva bitset
Es para vector<bool>
(que debe ser renombrado a bit_vector
— o lo que sea), como array
Es para vector
. Y la analogía debería ser cierta ya sea que estemos hablando o no 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. Actual demostrado positivo la experiencia de campo tiene una fuerte influencia. La gente de los estándares es un grupo conservador cuando se trata de cambiar las especificaciones existentes (como debería ser). Adivinar, incluso cuando está seguro de que está adivinando correctamente, es una estrategia arriesgada para desarrollar un estándar reconocido internacionalmente.
-
Pregunta: ¿podría/podría el reciente propuesta de borrador en los iteradores de proxy de @EricNiebler de alguna manera legitiman las extensiones libc++ y ponen
vector<bool>
en una base más de primera clase?– PlantillaRex
13 de agosto de 2015 a las 6:07
-
Observación: Preferiría tener un
vector_bool<Alloc>
y unarray_bool<N>
para representar versiones empaquetadas (incluidos los iteradores proxy de acceso aleatorio que iteran todos los bits) devector<bool, Alloc>
yarray<bool, N>
. Sin embargo,bitset<N>
(y es primoboost::dynamic_bitset<Alloc>
) representan una abstracción diferente: a saber, versiones empaquetadas destd::set<int>
. Así que me gustaría tener, digamos,bit_array<N>
ybit_vector<Alloc>
ser los sucesores de la franquicia bitset, con los bidireccional iteradores (iterando sobre los bits 1, en lugar de sobre todos los bits). ¿Cuáles son sus pensamientos sobre esto?– PlantillaRex
13 de agosto de 2015 a las 6:14
-
Mi borrador de propuesta sobre iteradores proxy haría
vector<bool>
un contenedor de acceso aleatorio conforme a std. no haríavector<bool>
una buena idea. 🙂 Estoy de acuerdo con Howard. Debería haberse llamado de otra manera.–Eric Niebler
13 de agosto de 2015 a las 18:57
-
¿Por qué no hay una manera de excluirse de las extensiones libc++ y obtener un comportamiento estrictamente conforme? (Ni siquiera pido que la conformidad sea la predeterminada, solo una forma de deshabilitar las extensiones de libc ++ para poder escribir código portátil). Como saben, me mordieron las extensiones de tupla libc++ en el pasado, y recientemente me mordió la extensión bitset::const_reference.
– gnzlbg
14 de agosto de 2015 a las 9:33
-
@gnzlbg: Se disponía de una cantidad finita de recursos económicos y temporales para el desarrollo inicial de libc++. Posteriormente, la implementación estaba condenada a no hacer felices a todos los usuarios. Dados los recursos disponibles, se realizaron compensaciones de ingeniería en un intento de maximizar la cantidad de usuarios satisfechos y maximizar el beneficio para la comunidad general de C++. Lamento tu experiencia. Observo que las extensiones de tupla con las que se encontró ahora están en el documento de trabajo actual de C++1z. En ese tema, sin saberlo, te sacrificaste para que muchos pudieran beneficiarse, y esos muchos te deben una deuda de gratitud.
– Howard Hinant
14/08/2015 a las 13:31