Альтернатива vector <bool>

92

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

  • Используйте vector<char>вместо этого, или
  • Используйте класс-оболочку и vector<bool_wrapper>

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

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

Редактировать:

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

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

struct my_bool
{
    bool the_bool;
};
vector<my_bool> haha_i_tricked_you;
rlbond
источник
2
Есть ли причина не использовать ... массив в стиле C?
kquinn
rlbond, вам нужен динамический размер?
Йоханнес Шауб - лит,
16
Хорошо, я укушу - как вы думаете, почему вектор «полностью сломан»?
Эндрю Грант,
8
@ Эндрю
Дэниел Эрвикер,
4
Интересно, vector<bool>что в моем коде только что возникла ошибка гонки данных, так как я ожидал, что разные потоки смогут безопасно изменять разные элементы вектора одновременно. Решено с помощью deque<bool>.
Андрес Риофрио

Ответы:

42

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

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

Дэниел Эрвикер
источник
21

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

Если вам нужно то, что было бы 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* 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 и конструкторами копирования по умолчанию. Я добавил сортировку, чтобы убедиться, что при использовании алгоритмов он реагирует так, как предполагается.

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

Клаим
источник
"мы могли бы добавить bool * operator & () {return & m_value;}" - err. ISO : « sizeof(bool)не обязательно 1»
Евгений Панасюк
2
Я бы предпочел , чтобы просто изменить operator bool() constАня operator bool&(). Это позволяет лучше отражать поведение простого bool, поскольку он поддерживает присваивание и т. Д. В таких случаях, как v[0] = true;я действительно не вижу проблемы с этим изменением, поэтому могу ли я внести изменения?
Agentlien
19

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

Давид Родригес - дрибеас
источник
unsigned charвсегда является одним байтом, хотя реализация uint8_tможет не поддерживаться. uint_fast8_tможет работать , хотя , если намерение состоит в том, чтобы сделать его ясно , что это один байт , а не символ, но вы можете также использовать std::byteзатем
Gabriel Ravier
13

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

boost::container::vector<bool>:

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

...

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

Евгений Панасюк
источник
6

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

Vector <char> или аналогичный будет использовать меньше места, хотя он также может получить (очень маленький) удар по скорости в некоторых обстоятельствах, потому что символы меньше размера машинного слова. Я считаю, что это основная причина того, что bools реализованы с использованием целых чисел вместо символов.

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

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

AHelps
источник
4

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

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

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

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

байда
источник
3

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

Тодд
источник
2

Я предпочитаю обходной путь - 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 );
Тони Э. Льюис
источник