Alternativa ao vetor <bool>

90

Como (esperançosamente) todos nós sabemos, vector<bool>está totalmente quebrado e não pode ser tratado como um array C. Qual é a melhor maneira de obter essa funcionalidade? Até agora, as ideias em que pensei são:

  • Use um vector<char>, ou
  • Use uma classe de wrapper e tenha vector<bool_wrapper>

Como vocês lidam com esse problema? Eu preciso da c_array()funcionalidade.

Como questão lateral, se eu não precisar do c_array()método, qual é a melhor maneira de abordar esse problema se eu precisar de acesso aleatório? Devo usar um deque ou outra coisa?

Editar:

  • Eu preciso de dimensionamento dinâmico.
  • Para quem não sabe, vector<bool>é especializado para que cada um boolleve 1 bit. Portanto, você não pode convertê-lo em uma matriz de estilo C.
  • Eu acho que "invólucro" é um pouco impróprio. Eu estava pensando algo assim:

Claro, então eu tenho que ler em um my_booldevido a possíveis problemas de alinhamento :(

struct my_bool
{
    bool the_bool;
};
vector<my_bool> haha_i_tricked_you;
rlbond
fonte
2
Existe algum motivo para não usar ... uma matriz de estilo C?
kquinn,
rlbond, você precisa de tamanho dinâmico?
Johannes Schaub - litb
16
Ok, vou morder - por que você acha que o vetor está "" totalmente quebrado "?
Andrew Grant,
8
@Andrew Grant - consulte open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2160.html
Daniel Earwicker
4
Curiosamente, vector<bool>acabei de causar um bug de corrida de dados no meu código, já que esperava que diferentes threads pudessem modificar diferentes elementos no vetor ao mesmo tempo com segurança. Resolvido usando deque<bool>.
Andres Riofrio,

Respostas:

40

Use std::dequese você não precisar do array, sim.

Caso contrário, use uma alternativa vectorque não seja especializada bool, como a do Boost Container .

Daniel Earwicker
fonte
21

Esse é um problema interessante.

Se você precisa do que seria um std :: vector se não fosse especializado, talvez algo assim funcione bem com o seu 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;
}

Tentei fazer isso com o VC9 e parece funcionar bem. A ideia da classe Bool é simular o tipo bool fornecendo o mesmo comportamento e tamanho (mas não o mesmo tipo). Quase todo o trabalho é feito pelo operador bool e pelos construtores de cópia padrão aqui. Eu adicionei uma classificação para ter certeza de que ela reagirá conforme o esperado ao usar algoritmos.

Não tenho certeza se serviria para todos os casos. Se for certo para suas necessidades, daria menos trabalho do que reescrever uma classe de vetor ...

Klaim
fonte
"poderíamos adicionar operador bool * & () {return & m_value;}" - err. ISO : " sizeof(bool)não é obrigatório 1"
Evgeny Panasyuk
2
Eu preferiria simplesmente alterar o operator bool() constpara um operator bool&(). Isso faz com que ele espelhe melhor o comportamento de um bool simples, pois oferece suporte a atribuição, etc., em casos como v[0] = true;eu realmente não consigo ver um problema com essa alteração, então posso fazer a edição?
Agentlien
19

Depende de suas necessidades. Eu iria para qualquer um std::vector<unsigned char>. Escrever um wrapper pode ser bom se você usar apenas um subconjunto da funcionalidade, caso contrário, se tornará um pesadelo.

David Rodríguez - dribeas
fonte
unsigned charé sempre um único byte, enquanto uint8_tpode não ser suportado pela implementação. uint_fast8_tpode funcionar, porém, se a intenção for deixar claro que é um único byte e não um caractere, mas você também pode usar std::byteentão
Gabriel Ravier
13

Como vocês lidam com esse problema? Preciso da funcionalidade c_array ().

boost::container::vector<bool>:

a especialização do vetor < bool > tem sido bastante problemática, e houve várias tentativas malsucedidas de descontinuá-lo ou removê-lo do padrão. Boost.Containernão o implementa porque há uma solução Boost.DynamicBitset superior .

...

Portanto, boost :: container :: vector :: iterator retorna referências reais de bool e funciona como um contêiner totalmente compatível. Se você precisar de uma versão otimizada para memória das funcionalidades boost :: container :: vector < bool >, use Boost.DynamicBitset .

Evgeny Panasyuk
fonte
6

Considere usar um vetor <int>. Depois de passar a compilação e a verificação de tipo, bool e int são apenas palavras de máquina (editar: aparentemente, isso nem sempre é verdade; mas será verdade em muitas arquiteturas de PC). Nos casos em que você deseja converter sem aviso, use "bool foo = !! bar", que converte zero em falso e diferente de zero em verdadeiro.

Um vetor <char> ou similar usará menos espaço, embora também tenha o potencial de sofrer um impacto de velocidade (muito pequeno) em algumas circunstâncias, porque os caracteres são menores que o tamanho da palavra da máquina. Esta é, creio eu, a principal razão pela qual os bools são implementados usando ints em vez de chars.

Se você realmente quiser uma semântica limpa, também gosto da sugestão de fazer sua própria classe booleana - parece um bool, age como um bool, mas engana a especialização do modelo.

Além disso, bem-vindo ao clube de pessoas que desejam que a especialização em vetor <bool> seja retirada do padrão C ++ (com bit_vector para substituí-lo). É onde todas as crianças legais se divertem :).

AAjuda
fonte
4

Este problema já foi discutido em comp.lang.c ++. Moderated. Soluções propostas:

  • seu próprio alocador (baseado em std::allocator) e sua própria especialização vetorial;
  • use std::deque(já foi recomendado em um dos livros S. Mayers) - mas não para suas necessidades;
  • fazer o boolinvólucro POD ;
  • utilização algo ( char/ int/ etc) com o mesmo tamanho que boolem vez bool;

Também no início eu vi uma proposta de comitê padrão - introduzir macro (algo como STD_VECTOR_BOOL_SPECIAL) para desautorizar esta especialização - mas AFAIK esta proposta não foi implementada em implementações de stl e não foi aprovada.

Parece que seu problema não tem como resolver isso direitinho ... Talvez em C ++ 0x.

Bayda
fonte
3

A resposta mais simples é usar vector<struct sb>onde sbestá struct {boolean b};. Então você pode dizer push_back({true}). Parece bom.

Todd
fonte
2

Minha solução alternativa preferida é um vectorenum com escopo definido que tem um tipo subjacente de bool. Isso chega bem perto do vector<bool>que teríamos se o comitê não tivesse se especializado nisso.

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 &> );

Você terá suas próprias opiniões sobre a sabedoria de abraçar moldes de / para 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
fonte