Alternativa al vector <bool>

92

Como (con suerte) todos sabemos, vector<bool>está totalmente roto y no se puede tratar como una matriz C. ¿Cuál es la mejor forma de obtener esta funcionalidad? Hasta ahora, las ideas en las que he pensado son:

  • Utilice a vector<char>en su lugar, o
  • Use una clase contenedora y tenga vector<bool_wrapper>

¿Cómo manejan ustedes este problema? Necesito la c_array()funcionalidad.

Como pregunta paralela, si no necesito el c_array()método, ¿cuál es la mejor manera de abordar este problema si necesito acceso aleatorio? ¿Debería usar un deque o algo más?

Editar:

  • Necesito un tamaño dinámico.
  • Para los que no lo saben, vector<bool>está especializado para que cadabool tome 1 bit. Por lo tanto, no puede convertirlo en una matriz de estilo C.
  • Supongo que "envoltorio" es un nombre poco apropiado. Estaba pensando en algo como esto:

Por supuesto, entonces tengo que leer un my_booldebido a posibles problemas de alineación :(

struct my_bool
{
    bool the_bool;
};
vector<my_bool> haha_i_tricked_you;
rlbond
fuente
2
¿Hay alguna razón para no usar ... una matriz de estilo C?
kquinn
rlbond, ¿necesita un tamaño dinámico?
Johannes Schaub - litb
16
Ok, morderé - ¿por qué crees que el vector está "" totalmente roto "?
Andrew Grant
8
@Andrew Grant - ver open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2160.html
Daniel Earwicker
4
Curiosamente, vector<bool>solo causé un error de carrera de datos en mi código, ya que esperaba que diferentes subprocesos pudieran modificar diferentes elementos en el vector al mismo tiempo de manera segura. Resuelto usando deque<bool>.
Andrés Riofrío

Respuestas:

42

Úselo std::dequesi no necesita la matriz, sí.

De lo contrario, use una alternativa en la vectorque no se especialice bool, como la de Boost Container .

Daniel Earwicker
fuente
21

Ese es un problema interesante.

Si necesita lo que hubiera sido un std :: vector si no estuviera especializado, entonces tal vez algo así funcionaría bien con su caso:

#include <vector>
#include <iostream> 
#include <algorithm>

class Bool
{
public:

    Bool(): m_value(){}
    Bool( bool value ) : m_value(value){}

    operator bool() const { return m_value; }

    // the following operators are to allow bool* b = &v[0]; (v is a vector here).
    bool* operator& () { return &m_value; }
    const bool* operator& () const { return &m_value; }

private:

    bool m_value;

};




int main()
{
    std::vector<Bool> working_solution(10, false);


    working_solution[5] = true;
    working_solution[7] = true;


    for( int i = 0; i < working_solution.size(); ++i )
    {
        std::cout<< "Id " << i << " = " << working_solution[i] << "(" <<(working_solution[i] ? "true" : "false") << ")" <<std::endl; // i used ? : to be sure the boolean evaluation is correct
    }

    std::sort( working_solution.begin(), working_solution.end());
    std::cout<< "--- SORTED! ---" << std::endl;

    for( int i = 0; i < working_solution.size(); ++i )
    {
            bool* b = &working_solution[i]; // this works!

        std::cout<< "Id " << i << " = " << working_solution[i] << "(" << (working_solution[i] ? "true" : "false") << ")" <<std::endl; // i used ? : to be sure the boolean evaluation is correct
    }

    std::cin.get();
    return 0;
}

Intenté esto con VC9 y parece funcionar bien. La idea de la clase Bool es simular el tipo bool proporcionando el mismo comportamiento y tamaño (pero no el mismo tipo). Casi todo el trabajo lo realiza el operador bool y los constructores de copia predeterminados aquí. Agregué un tipo para asegurarme de que reacciona como se supone cuando se usan algoritmos.

No estoy seguro de que se adapte a todos los casos. Si es adecuado para sus necesidades, sería menos trabajo que reescribir una clase similar a un vector ...

Klaim
fuente
"podríamos agregar bool * operator & () {return & m_value;}" - err. ISO : " sizeof(bool)no es obligatorio 1"
Evgeny Panasyuk
2
Preferiría simplemente cambiar el operator bool() consta operator bool&(). Esto hace que refleje mejor el comportamiento de un bool simple, ya que admite asignaciones, etc.en casos en los v[0] = true;que realmente no puedo ver un problema con este cambio, ¿puedo hacer la edición?
Agentlien
19

Depende de tus necesidades. Yo iría por cualquiera std::vector<unsigned char>. Escribir un contenedor puede estar bien si solo usa un subconjunto de la funcionalidad, de lo contrario se convertirá en una pesadilla.

David Rodríguez - dribeas
fuente
unsigned chares siempre de un solo byte, aunque es uint8_tposible que la implementación no lo admita. uint_fast8_taunque podría funcionar si la intención es dejar claro que es un solo byte y no un personaje, sino que también podría usar std::byteentonces
Gabriel Ravier
13

¿Cómo manejan ustedes este problema? Necesito la funcionalidad c_array ().

boost::container::vector<bool>:

La especialización vector < bool > ha sido bastante problemática, y ha habido varios intentos fallidos de desaprobarlo o eliminarlo del estándar. Boost.Containerno lo implementa ya que existe una solución Boost.DynamicBitset superior .

...

Así que boost :: container :: vector :: iterator devuelve referencias bool reales y funciona como un contenedor totalmente compatible. Si necesita una versión optimizada para la memoria de las funciones boost :: container :: vector < bool >, utilice Boost.DynamicBitset .

Evgeny Panasyuk
fuente
6

Considere usar un vector <int>. Una vez que pasa la compilación y la verificación de tipos, bool e int son solo palabras de máquina (editar: aparentemente esto no siempre es cierto; pero será cierto en muchas arquitecturas de PC). En aquellos casos en los que desee realizar una conversión sin una advertencia, utilice "bool foo = !! bar", que convierte cero en falso y no cero en verdadero.

Un vector <char> o similar usará menos espacio, aunque también tiene el potencial de recibir un golpe de velocidad (muy pequeño) en algunas circunstancias, porque los caracteres son menores que el tamaño de la palabra de la máquina. Esta es, creo, la razón principal por la que los bools se implementan usando ints en lugar de chars.

Si realmente quieres una semántica limpia, también me gusta la sugerencia de crear tu propia clase booleana: parece un bool, actúa como un bool, pero engaña a la especialización de la plantilla.

Además, bienvenido al club de personas que desean eliminar la especialización vector <bool> del estándar C ++ (con bit_vector para reemplazarla). Es donde pasan el rato todos los niños geniales :).

AHelps
fuente
4

Este problema ya se discutió en comp.lang.c ++. Moderated. Soluciones propuestas:

  • su propio asignador (basado en std::allocator) y su propia especialización vectorial;
  • uso std::deque(como antes se recomendó en uno de los libros de S. Mayers), pero esto no es para sus requisitos;
  • hacer boolenvoltura de POD ;
  • use algo ( char/ int/ etc) con el mismo tamaño que en su boollugar bool;

También temprano vi una propuesta para el comité estándar: introducir macro (algo como STD_VECTOR_BOOL_SPECIAL ) para no permitir esta especialización, pero AFAIK, esta propuesta no se implementó en implementaciones stl y no fue aprobada.

Parece que su problema no tiene forma de hacer esto bien ... Quizás en C ++ 0x.

Bayda
fuente
3

La respuesta más simple es usar vector<struct sb>where sbis struct {boolean b};. Entonces puedes decir push_back({true}). Parece bien.

Todd
fuente
2

Mi solución alternativa preferida es una vectorenumeración de ámbito que tiene un tipo subyacente de bool. Esto se acerca bastante al vector<bool>que hubiéramos tenido si el comité no lo hubiera especializado.

enum class switch_status : bool { ON, OFF };

static_assert( sizeof( switch_status ) == 1 );

::std::vector<switch_status> switches( 20, switch_status::ON );

static_assert( ::std::is_same_v< decltype( switches.front() ), switch_status &> );
static_assert( ::std::is_same_v< decltype( switches.back()  ), switch_status &> );
static_assert( ::std::is_same_v< decltype( switches[ 0 ]    ), switch_status &> );

Tendrás tus propias opiniones sobre la sabiduría de abrazar yesos hacia / desde bool:

enum class switch_status : bool { OFF = false, ON = true };

static_assert( static_cast< bool          >( switch_status::ON  ) == true               );
static_assert( static_cast< bool          >( switch_status::OFF ) == false              );
static_assert( static_cast< switch_status >( true               ) == switch_status::ON  );
static_assert( static_cast< switch_status >( false              ) == switch_status::OFF );
Tony E Lewis
fuente