Alternatywa dla wektora <bool>

92

Jak (miejmy nadzieję) wszyscy wiemy, vector<bool>jest całkowicie zepsuty i nie może być traktowany jako tablica C. Jaki jest najlepszy sposób na uzyskanie tej funkcjonalności? Do tej pory pomysły, o których myślałem, to:

  • Użyj vector<char>zamiast tego lub
  • Użyj klasy opakowania i użyj vector<bool_wrapper>

Jak radzicie sobie z tym problemem? Potrzebuję c_array()funkcjonalności.

Na marginesie, jeśli nie potrzebuję c_array()metody, jaki jest najlepszy sposób rozwiązania tego problemu, jeśli potrzebuję dostępu losowego? Powinienem użyć deque czy czegoś innego?

Edytować:

  • Potrzebuję dynamicznej zmiany rozmiaru.
  • Dla tych, którzy nie wiedzą, vector<bool>jest wyspecjalizowany, więc każdy boolzajmuje 1 bit. Dlatego nie możesz przekonwertować go na tablicę w stylu C.
  • Myślę, że „opakowanie” jest trochę mylące. Myślałem coś takiego:

Oczywiście muszę wtedy przeczytać my_boolze względu na możliwe problemy z wyrównaniem :(

struct my_bool
{
    bool the_bool;
};
vector<my_bool> haha_i_tricked_you;
rlbond
źródło
2
Czy jest jakiś powód, aby nie używać ... tablicy w stylu C?
kquinn
rlbond, czy potrzebujesz dynamicznego rozmiaru?
Johannes Schaub - litb
16
Ok, ugryzę - jak myślisz, dlaczego wektor jest „całkowicie zepsuty”?
Andrew Grant
8
@Andrew Grant - patrz open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2160.html
Daniel Earwicker
4
Co ciekawe, vector<bool>właśnie spowodowałem błąd wyścigu danych w moim kodzie, ponieważ spodziewałem się, że różne wątki będą w stanie jednocześnie bezpiecznie modyfikować różne elementy w wektorze. Rozwiązany za pomocą deque<bool>.
Andres Riofrio

Odpowiedzi:

42

Użyj, std::dequejeśli nie potrzebujesz tablicy, tak.

W przeciwnym razie użyj alternatywy vector, która nie jest specjalizująca się w tej dziedzinie bool, takiej jak ta w Kontenerze Wzmocnienia .

Daniel Earwicker
źródło
21

To ciekawy problem.

Jeśli potrzebujesz czegoś, co byłoby std :: vector, gdyby nie było wyspecjalizowane, może coś takiego działałoby dobrze w twoim przypadku:

#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;
}

Próbowałem tego z VC9 i wydaje się, że działa dobrze. Ideą klasy Bool jest symulowanie typu bool przez zapewnienie tego samego zachowania i rozmiaru (ale nie tego samego typu). Prawie cała praca jest wykonywana przez operatora bool i domyślne konstruktory kopiujące tutaj. Dodałem sortowanie, aby upewnić się, że zareaguje zgodnie z założeniami podczas korzystania z algorytmów.

Nie jestem pewien, czy będzie pasował do wszystkich przypadków. Jeśli jest odpowiedni dla twoich potrzeb, byłoby mniej pracy niż przepisanie klasy wektorowej ...

Klaim
źródło
"moglibyśmy dodać operator bool * & () {return & m_value;}" - błąd. ISO : „ sizeof(bool)nie musi być 1
Evgeny Panasyuk,
2
Wolałbym po prostu zmienić operator bool() constna operator bool&(). To sprawia, że ​​lepiej odzwierciedla zachowanie prostej wartości logicznej, ponieważ obsługuje przypisanie itp. W przypadkach, gdy v[0] = true;naprawdę nie widzę problemu z tą zmianą, więc czy mogę dokonać edycji?
Agentlien
19

Zależy od Twoich potrzeb. Zrobiłbym to std::vector<unsigned char>. Napisanie opakowania może być w porządku, jeśli używasz tylko części funkcji, w przeciwnym razie stanie się koszmarem.

David Rodríguez - dribeas
źródło
unsigned charjest zawsze jednobajtowa, podczas gdy uint8_timplementacja może nie być obsługiwana. uint_fast8_tmoże zadziałać, jeśli intencją jest wyjaśnienie, że jest to pojedynczy bajt, a nie znak, ale równie dobrze możesz użyć std::bytewtedy
Gabriel Ravier,
13

Jak radzicie sobie z tym problemem? Potrzebuję funkcji c_array ().

boost::container::vector<bool>:

specjalizacja vector < bool > była dość problematyczna i było kilka nieudanych prób jej wycofania lub usunięcia ze standardu. Boost.Containernie implementuje go, ponieważ istnieje lepsze rozwiązanie Boost.DynamicBitset .

...

Tak więc boost :: container :: vector :: iterator zwraca rzeczywiste odwołania bool i działa jako w pełni zgodny kontener. Jeśli potrzebujesz zoptymalizowanej pod kątem pamięci wersji funkcjonalności boost :: container :: vector < bool >, użyj Boost.DynamicBitset .

Evgeny Panasyuk
źródło
6

Rozważ użycie wektora <int>. Gdy przejdziesz przez kompilację i sprawdzanie typów, bool i int są tylko słowami maszynowymi (edycja: najwyraźniej nie zawsze jest to prawda; ale będzie prawdą na wielu architekturach komputerów PC). W przypadkach, w których chcesz dokonać konwersji bez ostrzeżenia, użyj „bool foo = !! bar”, które konwertuje zero na fałsz i niezerowe na prawdę.

Wektor <char> lub podobny będzie zużywał mniej miejsca, chociaż może również w pewnych okolicznościach spowodować (bardzo małą) szybkość, ponieważ znaki są mniejsze niż rozmiar słowa maszynowego. To jest, jak sądzę, główny powód, dla którego boole są implementowane przy użyciu ints zamiast chars.

Jeśli naprawdę chcesz czystej semantyki, podoba mi się również sugestia stworzenia własnej klasy boolowskiej - wygląda jak bool, działa jak bool, ale oszukuje specjalizację szablonu.

Witamy w gronie ludzi, którzy chcą, aby specjalizacja vector <bool> została usunięta ze standardu C ++ (z bit_vector, aby ją zastąpić). Tutaj spędzają czas wszystkie fajne dzieciaki :).

AHelps
źródło
4

Ten problem był już omawiany na comp.lang.c ++. Moderated. Proponowane rozwiązania:

  • własny alokator (w oparciu o std::allocator) i własną specjalizację wektorów;
  • zastosowanie std::deque(jak wcześnie było zalecane w jednej z książek S. Mayersa) - ale to nie do twoich wymagań;
  • zrobić boolopakowanie POD ;
  • użyj czegoś ( char/ int/ etc) o takim samym rozmiarze jak boolzamiast tego bool;

Wcześnie też spotkałem się z propozycją komitetu standaryzacyjnego - wprowadzić makro (coś w rodzaju STD_VECTOR_BOOL_SPECIAL), żeby nie zezwalać na tę specjalizację - ale AFAIK ta propozycja nie została zaimplementowana w implementacjach STL i nie została zatwierdzona.

Wygląda na to, że twój problem nie ma sposobu, aby to ładnie zrobić ... Może w C ++ 0x.

bayda
źródło
3

Najprostsza odpowiedź brzmi: vector<struct sb>gdzie sbjest struct {boolean b};. Wtedy możesz powiedzieć push_back({true}). Wygląda dobrze.

Todd
źródło
2

Moim preferowanym obejściem jest vectorwyliczenie o określonym zakresie, które ma podstawowy typ bool. Jest to bardzo zbliżone do tego, vector<bool>które mielibyśmy, gdyby komitet nie wyspecjalizował tego.

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

Będziesz mieć własne opinie na temat mądrości przyjmowania rzutów do / z 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
źródło