2011-12-06 7 views
46
#include <vector> 

struct A 
{ 
    void foo(){} 
}; 

template< typename T > 
void callIfToggled(bool v1, bool &v2, T & t) 
{ 
    if (v1 != v2) 
    { 
     v2 = v1; 
     t.foo(); 
    } 
} 

int main() 
{ 
    std::vector<bool> v= { false, true, false }; 

    const bool f = false; 
    A a; 

    callIfToggled(f, v[0], a); 
    callIfToggled(f, v[1], a); 
    callIfToggled(f, v[2], a); 
} 

La compilación del ejemplo anterior produce el error siguiente:¿Por qué el vector <bool> :: reference no devuelve referencia a bool?

dk2.cpp: In function 'int main()': 
dk2.cpp:29:28: error: no matching function for call to 'callIfToggled(const bool&, std::vector<bool>::reference, A&)' 
dk2.cpp:29:28: note: candidate is: 
dk2.cpp:13:6: note: template<class T> void callIfToggled(bool, bool&, T&) 

He compilado usando g ++ (versión 4.6.1) así:

g++ -O3 -std=c++0x -Wall -Wextra -pedantic dk2.cpp 

La cuestión es por qué sucede esto? ¿vector<bool>::reference no es bool&? ¿O es un error del compilador?
O, ¿estoy intentando algo estúpido? :)

+12

Desafortunadamente, a pesar de su nombre, 'std :: vector ' no es un 'vector' de' bool'. –

+1

Como solución alternativa, se puede usar 'std :: unique_ptr (nueva bool [3])' ... –

+2

de Herb Sutter [¿Cuándo un contenedor no un contenedor?] (Http://www.gotw.ca/publications /mill09.htm) es solo acerca de este problema. – legends2k

Respuesta

49

Vector is specialized for bool.

Se considera un error de la norma. vector<char> utilizar en su lugar:

template<typename t> 
struct foo { 
    using type = t; 
}; 
template<> 
struct foo<bool> { 
    using type = char; 
}; 

template<typename t, typename... p> 
using fixed_vector = std::vector<typename foo<t>::type, p...>; 

De vez en cuando es posible que tenga referencias a un bool contenida dentro del vector. Desafortunadamente, usar vector<char> solo puede darle referencias a caracteres. Si realmente necesita bool&, consulte el Boost Containers library. Tiene una versión no especializada de vector<bool>.

+3

Bonita solución y buena demostración de la nueva sintaxis 'using' para aliasing. –

+5

+1 para el uso * inteligente * del 'using' (sin juego de palabras). – Nawaz

+0

@Nawaz Juro por Dios que el juego de palabras fue intencionado. –

15

std::vector<bool> es un contenedor no conforme. Para optimizar el espacio, contiene bool sy no puede proporcionar una referencia.

Use boost::dynamic_bitset en su lugar.

+0

Para obtener un tipo de referencia, necesita usar el operador [] (result es dynamic_bitset :: reference). Sin embargo, no hay iterador. – reder

+2

-1 por no mencionar cómo 'dynamic_bitset' es diferente o mejor. Por supuesto, tampoco puede devolver un 'bool &'. – Potatoswatter

44

Sus expectativas son normales, pero el problema es que std::vector<bool> ha sido un tipo de experimento por parte del comité de C++. En realidad, se trata de una especialización de plantillas que almacena los valores de bool empaquetados en la memoria: un bit por valor.

Y como no puede tener una referencia, existe su problema.

+7

+1. std :: vector solo admite un subconjunto de la funcionalidad proporcionada por std :: vector. Es feo, ya que tienes que reemplazar bool con un tipo diferente (char) para obtener un vector que funcione. – josefx

+1

¡Un experimento que la mayoría de los miembros del comité (si no todos) lamentaron! – curiousguy

5

Sólo mi 2 centavos:

std::vector<bool>::reference es un typedef para struct _Bit_reference que se define como

typedef unsigned long _Bit_type; 

struct _Bit_reference 
    { 
    _Bit_type * _M_p; 
    _Bit_type _M_mask; 

    // constructors, operators, etc... 

    operator bool() const 
    { return !!(*_M_p & _M_mask); } 
    }; 

Cambio de la función como esta, funciona (bueno, compila al menos, no se han probado) :

template< typename T > 
void callIfToggled(bool v1, std::vector<bool>::reference v2, T & t) 
{ 
    bool b = v2; 
    if (v1 != b) 
    { 
     v2 = v1; 
     t.foo(); 
    } 
} 

EDIT: (! = v1 v2) (! v1 = b) he cambiado la condición de que no era una buena idea, a.

+1

Funciona, pero ¿es esta una extensión de g ++? –

+1

No es una extensión, es solo cómo GCC implementa la especialización vector . No sé qué dice el estándar sobre esto. Puede verlo usted mismo: std_bvector.h en lib/gcc/mingw32/4.6.1/include/C++/bits. (Su árbol de directorio puede ser diferente, pero probablemente similar) – jrok

+0

Claro, funciona, para este caso específico - además de ilustrar por qué 'vector ' es un término inapropiado, ya que se vuelve increíblemente difícil, tedioso o imposible de usar en genéricos situaciones de código que funcionan para otros, _actual_ contenedores. Aún así, creo que una solución utilizable aquí sería usar 'template void callIfToggled (B && v1, B && v2, T && t)' y confiar en el operador de conversión de 'v2' - que supongo que es otro razón para estar agradecido por reenviar referencias. ¡Sin embargo, no excusa la elección incorrecta del nombre! –

13

std::vector<bool> empaqueta su contenido de modo que cada valor booleano se almacena en un bit, ocho bits en un byte. Esto es eficiente desde el punto de vista de la memoria, pero computacionalmente intensivo, ya que el procesador debe realizar operaciones aritméticas para acceder al bit solicitado. Y no funciona con bool referencia o semántica de puntero, ya que los bits dentro de un byte no tienen direcciones en el modelo de objetos C++.

Aún puede declarar una variable del tipo std::vector<bool>::reference y utilizarla como si fuera bool&. Esto permite que los algoritmos genéricos sean compatibles.

std::vector<bool> bitvec(22); 
std::vector<bool>::reference third = bitvec[ 2 ]; 
third = true; // assign value to referenced bit 

En C++ 11, se puede trabajar alrededor de este uso de auto y el especificador de && que selecciona automáticamente una referencia lvalue unido al elemento del vector o una referencia rvalue unido a un temporal.

std::vector<bool> bitvec(22); 
auto &&third = bitvec[ 2 ]; // obtain a std::vector<bool>::reference 
third = true; // assign value to referenced bit 
+0

Buena respuesta, especialmente la mención de '&&', que es crucial para el código genérico para permitir que los tipos de proxy/iteradores alguna vez sean útiles. Por supuesto, funciona igual de bien en bucles: 'for (auto && it: bizarreContainer)' –

1

hacer una estructura con un bool en ella, y crea la vector<> uso de ese tipo de estructura.

Probar:

vector<struct sb> donde sb es struct {boolean b];

entonces se puede decir

push_back({true})

hacer

typedef struct sbool {bool b;} boolstruct; y luego vector<boolstruct> bs;

Cuestiones relacionadas