Альтернатива вектору <bool> - PullRequest
84 голосов
/ 22 марта 2009

Как (надеюсь) мы все знаем, vector<bool> полностью сломан и не может рассматриваться как массив c. Каков наилучший способ получить эту функциональность? До сих пор я думал о следующих идеях:

  • Используйте взамен vector<char> или
  • Использовать класс-обёртку и иметь vector<bool_wrapper>

Как вы, ребята, справляетесь с этой проблемой? Мне нужна функциональность c_array ().

В качестве дополнительного вопроса, если мне не нужен метод c_array (), как лучше всего решить эту проблему, если мне нужен произвольный доступ? Должен ли я использовать deque или что-то еще?

Edit:

  • Мне нужен динамический размер.
  • Для тех, кто не знает, vector<bool> специализируется, так что каждый бул занимает 1 бит. Таким образом, вы не можете преобразовать его в массив в стиле c.
  • Я полагаю, что "обертка" немного неправильная. Я думал что-то вроде этого:

Конечно, тогда я должен прочитать в my_bool из-за возможных проблем с выравниванием: (

struct my_bool
{
    bool the_bool;
};
vector<my_bool> haha_i_tricked_you;

Ответы [ 8 ]

38 голосов
/ 22 марта 2009

Используйте std::deque, если вам не нужен массив, да.

В противном случае используйте альтернативу vector, которая не специализируется на bool, такую ​​как в Boost Container .

20 голосов
/ 22 марта 2009

Это интересная проблема.

Если вам нужно что-то, что было бы std :: vector, если бы оно не было специализированным, то, возможно, что-то подобное будет работать в вашем случае:

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

Я пробовал это с VC9, и, кажется, работает нормально. Идея класса Bool состоит в том, чтобы моделировать тип bool, предоставляя такое же поведение и размер (но не один и тот же тип). Почти вся работа выполняется оператором bool и конструкторами копирования по умолчанию. Я добавил сортировку, чтобы убедиться, что она реагирует так, как предполагалось при использовании алгоритмов.

Не уверен, что это подойдет для всех случаев. Если это подходит для ваших нужд, это будет меньше работы, чем переписать вектороподобный класс ...

16 голосов
/ 22 марта 2009

Зависит от ваших потребностей. Я бы выбрал либо std::vector<unsigned char>. Написание оболочки может быть хорошо, если вы используете только часть функций, иначе это станет кошмаром.

11 голосов
/ 12 апреля 2013

Как вы, ребята, справляетесь с этой проблемой? Мне нужна функциональность c_array ().

boost::container::vector<bool>

vector <<strong> bool > специализация была довольно проблематичной, и было несколько неудачных попыток отменить или удалить ее из стандарта. Boost.Container не реализует его, поскольку существует превосходное решение Boost.DynamicBitset .

...

Итак, boost :: container :: vector :: iterator возвращает реальные ссылки bool и работает как полностью совместимый контейнер. Если вам нужна оптимизированная для памяти версия функций boost :: container :: vector <<strong> bool >, используйте Boost.DynamicBitset .

5 голосов
/ 22 марта 2009

Рассмотрите возможность использования вектора . После прохождения компиляции и проверки типов, bool и int являются просто машинными словами (правка: очевидно, это не всегда так; но будет верно для многих архитектур ПК). В тех случаях, когда вы хотите конвертировать без предупреждения, используйте «bool foo = !! bar», который преобразует ноль в ложь и ненулевой в истину.

Вектор или аналогичный будет занимать меньше места, хотя в некоторых случаях он также может получить (очень малый) удар скорости, потому что символы меньше размера машинного слова. Я полагаю, что это главная причина того, что в bool-файлах используются целые числа вместо символов.

Если вы действительно хотите чистую семантику, мне также нравится предложение создать свой собственный логический класс - выглядит как bool, действует как bool, но обманывает специализацию шаблона.

Кроме того, добро пожаловать в клуб людей, которые хотят, чтобы специализация vector была исключена из стандарта C ++ (с заменой на bit_vector). Здесь тусуются все крутые детки :).

4 голосов
/ 24 июня 2015

Простейшим ответом является использование vector<struct sb>, где sb равно struct {boolean b};. Тогда вы можете сказать push_back({true}). Вроде хорошо.

4 голосов
/ 22 марта 2009

Эта проблема уже обсуждалась на comp.lang.c ++., Модерируемой. Предлагаемые решения:

  • ваш собственный распределитель (на основе std::allocator) и собственная векторная специализация;
  • используйте std::deque (как ранее было рекомендовано в одной из книг С. Майерса) - но это не для ваших требований;
  • make POD bool обертка;
  • использовать что-то (char / int / etc) с тем же размером, что и bool вместо bool;

Также рано я увидел предложение для стандартного комитета - ввести макрос (что-то вроде STD_VECTOR_BOOL_SPECIAL), чтобы запретить эту специализацию - но AFAIK это предложение не было реализовано в реализациях stl и не было утверждено.

Кажется, у вашей проблемы нет способов сделать это красиво ... Может быть, в C ++ 0x.

2 голосов
/ 05 октября 2018

Мой предпочтительный обходной путь - vector перечисления с областью действия, который имеет базовый тип bool. Это довольно близко к vector<bool>, которое было бы у нас, если бы комитет не специализировал его.

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

У вас будет собственное мнение о мудрости принятия заклинаний в / из 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 );
...