Альтернатива векторному <bool>


92

Як (сподіваємось) ми всі знаємо, vector<bool>він повністю зламаний і не може розглядатися як масив C. Який найкращий спосіб отримати цю функціональність? Поки що ідеями, про які я думав, є:

  • Використовуйте vector<char>замість цього, або
  • Використовуйте клас обгортки та маєте vector<bool_wrapper>

Як ви, хлопці, вирішуєте цю проблему? Мені потрібна c_array()функціональність.

Як побічне запитання, якщо мені c_array()метод не потрібен , який найкращий спосіб вирішити цю проблему, якщо мені потрібен довільний доступ? Чи слід використовувати декель чи щось інше?

Редагувати:

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

Звичайно, тоді мені доведеться прочитати проект my_boolчерез можливі проблеми вирівнювання :(

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

2
Чи є якась причина не використовувати ... масив у стилі С?
kquinn

rlbond, вам потрібен динамічний розмір?
Йоханнес Шауб - літ

16
Гаразд, я вкушу - чому, на вашу думку, вектор "" повністю зламаний "?
Ендрю Грант,


4
Цікаво, що vector<bool>саме в моєму коді виникла помилка гонки даних, оскільки я очікував, що різні потоки зможуть безпечно одночасно змінювати різні елементи у векторі. Вирішено за допомогою deque<bool>.
Андрес Ріофріо,

Відповіді:



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 * & () {return & m_value;}" - помилка. ISO : " sizeof(bool)не потрібно 1"
Євген Панасюк

2
Я хотів би просто змінити його operator bool() constна operator bool&(). Це робить це краще відображає поведінку простого bool, оскільки він підтримує призначення і т. Д. У таких випадках, як v[0] = true;я дійсно не бачу проблеми з цією зміною, так що я можу внести зміни?
Агентлієн

19

Залежить від ваших потреб. Я б пішов на будь-яке std::vector<unsigned char>. Написати обгортку може бути добре, якщо ви використовуєте лише підмножину функціоналу, інакше це стане кошмаром.


unsigned charзавжди є одним байтом, хоча реалізація uint8_tможе не підтримуватися. uint_fast8_tможе працювати, хоча якщо намір дати зрозуміти, що це один байт, а не символ, але ви могли б використовувати і std::byteтоді
Габріель Рав'є

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", який перетворює нуль на false і ненульове значення на true.

Вектор <char> або подібний використовуватиме менше місця, хоча він також має потенціал прийняти (дуже малу) швидкість удару за деяких обставин, оскільки символи менше розміру машинного слова. Це, я вважаю, головна причина того, що були реалізовані з використанням ints замість символів.

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

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


4

Цю проблему вже обговорювали на comp.lang.c ++. Модерується. Пропоновані рішення:

  • власний розподільник (на основі std::allocator) та власна спеціалізація на векторах;
  • використання std::deque(як це було рекомендовано в одній з книг С. Майєрса) - але це не для ваших вимог;
  • зробити boolобгортку POD ;
  • використовувати щось ( char/ int/ і т.д.) із таким самим розміром, як 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 );
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.