Alternative zum Vektor <bool>

90

Wie (hoffentlich) wir alle wissen, vector<bool>ist es total kaputt und kann nicht als C-Array behandelt werden. Was ist der beste Weg, um diese Funktionalität zu erhalten? Bisher habe ich an folgende Ideen gedacht:

  • Verwenden Sie vector<char>stattdessen ein oder
  • Verwenden Sie eine Wrapper-Klasse und haben vector<bool_wrapper>

Wie geht ihr mit diesem Problem um? Ich brauche die c_array()Funktionalität.

Als Nebenfrage: Wenn ich die c_array()Methode nicht benötige , wie kann ich dieses Problem am besten angehen, wenn ich wahlfreien Zugriff benötige? Soll ich eine Deque oder etwas anderes verwenden?

Bearbeiten:

  • Ich brauche eine dynamische Dimensionierung.
  • Für diejenigen, die es nicht wissen, vector<bool>ist spezialisiert, so dass jedes bool1 Bit dauert. Daher können Sie es nicht in ein Array im C-Stil konvertieren.
  • Ich denke, "Wrapper" ist ein bisschen eine Fehlbezeichnung. Ich dachte so etwas:

Natürlich muss ich dann my_boolaufgrund möglicher Ausrichtungsprobleme in ein lesen :(

struct my_bool
{
    bool the_bool;
};
vector<my_bool> haha_i_tricked_you;
rlbond
quelle
2
Gibt es einen Grund, kein Array im C-Stil zu verwenden?
Kquinn
rlbond, brauchst du eine dynamische größe?
Johannes Schaub - litb
16
Ok, ich werde beißen - warum denkst du, ist der Vektor "total kaputt"?
Andrew Grant
8
@ Andrew Grant - siehe open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2160.html
Daniel Earwicker
4
Interessanterweise hat vector<bool>gerade ein Datenrennen-Fehler in meinem Code verursacht, da ich erwartet hatte, dass verschiedene Threads verschiedene Elemente im Vektor gleichzeitig sicher ändern können. Gelöst mit deque<bool>.
Andres Riofrio

Antworten:

40

Verwenden std::dequeSie diese Option, wenn Sie das Array nicht benötigen.

Verwenden Sie andernfalls eine Alternative vector, auf die Sie sich nicht spezialisiert haben bool, z. B. die im Boost-Container .

Daniel Earwicker
quelle
21

Das ist ein interessantes Problem.

Wenn Sie einen std :: vector benötigen, der nicht spezialisiert wäre, funktioniert so etwas möglicherweise gut für Ihren Fall:

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

Ich habe dies mit VC9 versucht und es scheint gut zu funktionieren. Die Idee der Bool-Klasse besteht darin, den Bool-Typ zu simulieren, indem das gleiche Verhalten und die gleiche Größe (jedoch nicht der gleiche Typ) bereitgestellt werden. Fast die gesamte Arbeit wird hier vom Bool-Operator und den Standard-Kopierkonstruktoren ausgeführt. Ich habe eine Sortierung hinzugefügt, um sicherzustellen, dass sie bei Verwendung von Algorithmen wie angenommen reagiert.

Ich bin mir nicht sicher, ob es für alle Fälle geeignet ist. Wenn es für Ihre Bedürfnisse richtig ist, wäre es weniger Arbeit als das Umschreiben einer vektorähnlichen Klasse ...

Klaim
quelle
"Wir könnten den Bool * -Operator & () {return & m_value;} hinzufügen" - err. ISO : "muss sizeof(bool)nicht sein 1"
Evgeny Panasyuk
2
Ich würde es vorziehen, einfach das operator bool() constzu ändern operator bool&(). Dadurch spiegelt es das Verhalten eines einfachen Bools besser wider, da es die Zuweisung usw. unterstützt, wenn v[0] = true;ich wirklich kein Problem mit dieser Änderung sehe. Darf ich also die Bearbeitung vornehmen?
Agentlien
19

Kommt auf deine Bedürfnisse an. Ich würde für beide gehen std::vector<unsigned char>. Das Schreiben eines Wrappers kann in Ordnung sein, wenn Sie nur eine Teilmenge der Funktionalität verwenden, da dies sonst zu einem Albtraum wird.

David Rodríguez - Dribeas
quelle
unsigned charist immer ein einzelnes Byte, wird jedoch uint8_tmöglicherweise von der Implementierung nicht unterstützt. uint_fast8_tkönnte zwar funktionieren , wenn die Absicht ist es deutlich zu machen , es ist ein einziges Byte ist und kein Zeichen, aber Sie könnten als auch verwenden , std::bytedann
Gabriel Ravier
13

Wie geht ihr mit diesem Problem um? Ich benötige die Funktion c_array ().

boost::container::vector<bool>::

Die Vektor < bool > -Spezialisierung war ziemlich problematisch, und es gab mehrere erfolglose Versuche, sie zu verwerfen oder aus dem Standard zu entfernen. Boost.Containerimplementiert es nicht, da es eine überlegene Boost.DynamicBitset- Lösung gibt.

...

So boost :: Container :: vector :: iterator liefert echte Bool Referenzen und arbeitet als voll kompatibel Behälter. Wenn Sie eine speicheroptimierte Version der Funktionen boost :: container :: vector < bool > benötigen , verwenden Sie bitte Boost.DynamicBitset .

Evgeny Panasyuk
quelle
6

Verwenden Sie einen Vektor <int>. Sobald Sie die Kompilierung und Typprüfung hinter sich gelassen haben, sind bool und int nur noch Maschinenwörter (bearbeiten: Anscheinend ist dies nicht immer der Fall; wird aber auf vielen PC-Architekturen zutreffen). In den Fällen, in denen Sie ohne Warnung konvertieren möchten, verwenden Sie "bool foo = !! bar", das Null in falsch und Nicht-Null in wahr konvertiert.

Ein Vektor <char> oder ähnliches benötigt weniger Platz, kann jedoch unter bestimmten Umständen auch einen (sehr kleinen) Geschwindigkeitstreffer verursachen, da die Zeichen kleiner als die Maschinenwortgröße sind. Dies ist meiner Meinung nach der Hauptgrund dafür, dass Bools mit Ints anstelle von Zeichen implementiert werden.

Wenn Sie wirklich eine saubere Semantik wollen, mag ich auch den Vorschlag, eine eigene Boolesche Klasse zu erstellen - sieht aus wie ein Bool, verhält sich wie ein Bool, täuscht aber die Template-Spezialisierung.

Willkommen auch im Club der Leute, die möchten, dass die Vektor <bool> -Spezialisierung aus dem C ++ - Standard gestrichen wird (mit bit_vector als Ersatz). Hier hängen all die coolen Kids rum :).

AHelps
quelle
4

Dieses Problem wurde bereits in comp.lang.c ++. Moderiert diskutiert . Lösungsvorschläge:

  • Ihr eigener Allokator (basierend auf std::allocator) und Ihre eigene Vektorspezialisierung;
  • Verwendung std::deque(wie früh in einem der Bücher S. Mayers empfohlen wurde) - aber dies nicht für Ihre Anforderungen;
  • POD- boolWrapper herstellen;
  • Verwenden Sie stattdessen etwas ( char/ int/ etc) mit der gleichen Größe wie ; boolbool

Ebenfalls früh sah ich einen Vorschlag für ein Standardkomitee - Einführung eines Makros (so etwas wie STD_VECTOR_BOOL_SPECIAL), um diese Spezialisierung nicht zuzulassen -, aber AFAIK dieser Vorschlag wurde nicht in STL-Implementierungen implementiert und nicht genehmigt.

Es scheint, dass Ihr Problem keine Möglichkeit hat, dies gut zu machen ... Vielleicht in C ++ 0x.

Bayda
quelle
3

Die einfachste Antwort ist verwenden, vector<struct sb>wo sbist struct {boolean b};. Dann kannst du sagen push_back({true}). Es scheint gut.

Todd
quelle
2

Meine bevorzugte Problemumgehung ist eine vectorAufzählung mit Gültigkeitsbereich, der ein zugrunde liegender Typ von bool. Dies kommt dem sehr nahe, was vector<bool>wir gehabt hätten, wenn das Komitee es nicht spezialisiert hätte.

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

Sie werden Ihre eigene Meinung über die Weisheit haben, Casts zu / von zu umarmen 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
quelle