Почему вектор <bool> :: const_reference в libc ++ не является bool?

92

Раздел 23.3.7 Класс vector<bool>[vector.bool], параграф 1 гласит:

template <class Allocator> class vector<bool, Allocator> {
public:
    // types:
    typedef bool              const_reference;
    ...

Однако эта программа не компилируется при использовании libc ++:

#include <vector>
#include <type_traits>

int
main()
{
    static_assert(std::is_same<std::vector<bool>::const_reference, bool>{}, "?");
}

Кроме того, я отмечаю, что стандарт C ++ был согласован в этой спецификации вплоть до C ++ 98. И я также отмечаю, что libc ++ последовательно не следовала этой спецификации с момента первого введения libc ++.

Какова мотивация этого несоответствия?

Говард Хиннант
источник

Ответы:

99

Мотивация для этого расширения, которое может быть обнаружено соответствующей программой и, следовательно, не соответствует требованиям, состоит в том, чтобы заставить vector<bool>вести себя как в vector<char>отношении ссылок (const и в противном случае).

Введение

С 1998 года vector<bool>его высмеивали как «не совсем контейнер». LWG 96 , одна из самых первых проблем LWG, инициировала дебаты. Сегодня, 17 лет спустя, vector<bool>практически ничего не изменилось.

В этой статье приводятся некоторые конкретные примеры того, как поведение vector<bool>отличается от любого другого экземпляра vector, что наносит ущерб общему коду. Однако в той же статье подробно обсуждаются очень хорошие рабочие характеристики, которые vector<bool>могут быть получены при правильной реализации.

Резюме : vector<bool>неплохой контейнер. На самом деле это очень полезно. Просто у него плохая репутация.

Вернуться к const_reference

Как было сказано выше и подробно описано здесь , плохим vector<bool>то, что он ведет себя в универсальном коде иначе, чем в других vectorэкземплярах. Вот конкретный пример:

#include <cassert>
#include <vector>

template <class T>
void
test(std::vector<T>& v)
{
    using const_ref = typename std::vector<T>::const_reference;
    const std::vector<T>& cv = v;
    const_ref cr = cv[0];
    assert(cr == cv[0]);
    v[0] = 1;
    assert(true == cv[0]);
    assert(cr == cv[0]);  // Fires!
}

int
main()
{
    std::vector<char> vc(1);
    test(vc);
    std::vector<bool> vb(1);
    test(vb);
}

Стандартная спецификация говорит, что отмеченное утверждение // Fires!будет срабатывать, но только когда testоно запускается с vector<bool>. При запуске с vector<char>(или любым vectorдругим, boolесли назначено соответствующее значение, отличное от значения по умолчанию T), тест проходит.

Реализация libc ++ стремилась свести к минимуму негативные последствия vector<bool>различного поведения в общем коде. Единственное , что он сделал , чтобы добиться этого , чтобы сделать vector<T>::const_referenceна прокси-ссылки , так же , как указано vector<T>::reference, за исключением того, что вы не можете назначить через него. То есть в libc ++ это, vector<T>::const_referenceпо сути, указатель на бит внутри vector, а не копия этого бита.

В libc ++ вышеуказанное testподходит как для, так vector<char>и для vector<bool>.

Какой ценой?

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

Какова мотивация этого несоответствия?

Чтобы улучшить поведение клиента libc ++ в общем коде и, возможно, после достаточного полевого тестирования, предложите это расширение для будущего стандарта C ++ для улучшения всей индустрии C ++.

Такое предложение могло бы появиться в форме нового контейнера (например bit_vector), который будет иметь почти такой же API, как сегодняшний vector<bool>, но с некоторыми обновлениями, такими как const_referenceобсуждаемые здесь. За этим следует прекращение поддержки (и возможное удаление) vector<bool>специализации. bitsetтакже можно было бы использовать небольшое обновление в этом отделе, например, добавить const_referenceи набор итераторов.

То есть в ретроспективе bitsetэто to vector<bool>(которое следует переименовать в bit_vector- или что-то еще), as arrayis to vector. И аналогия должна верны ли или нет , мы говорим о том, boolкак value_typeиз vectorи array.

Есть несколько примеров функций C ++ 11 и C ++ 14, которые начинались как расширения в libc ++. Так развиваются стандарты. Фактический продемонстрированный положительный полевой опыт оказывает сильное влияние. Специалисты по стандартам - консервативная группа, когда дело доходит до изменения существующих спецификаций (как и должно быть). Гадание, даже если вы уверены, что угадываете правильно, - рискованная стратегия для развития международно признанного стандарта.

Говард Хиннант
источник
1
Вопрос: может ли недавний проект предложения по итераторам прокси от @EricNiebler каким-либо образом узаконить расширения libc ++ и поставить их vector<bool>на более первоклассную основу?
TemplateRex
Замечание: я бы предпочел иметь a vector_bool<Alloc>и an array_bool<N>для представления упакованных версий (включая итераторы прокси с произвольным доступом, повторяющие все биты) vector<bool, Alloc>и array<bool, N>. Однако bitset<N>(и его двоюродный брат boost::dynamic_bitset<Alloc>) представляют собой другую абстракцию: а именно упакованные версии std::set<int>. Так что я хотел бы иметь, скажем, bit_array<N>и bit_vector<Alloc>быть преемниками франшизы битовых наборов, с соответствующими двунаправленными итераторами (итерация по 1-битам, а не по всем битам). Что вы думаете об этом?
TemplateRex
5
В моем проекте предложения по итераторам прокси будет vector<bool>создан контейнер произвольного доступа, соответствующий стандарту. Это не vector<bool>лучшая идея. :-) Я согласен с Говардом. Его следовало назвать иначе.
Эрик Ниблер,
1
Почему нет способа отказаться от расширений libc ++ и добиться строго соответствующего поведения? (Я даже не прошу установить соответствие по умолчанию, просто способ отключить расширения libc ++, чтобы иметь возможность писать переносимый код). Как вы знаете, в прошлом меня кусали расширения кортежей libc ++, а недавно - расширение bitset :: const_reference.
gnzlbg
5
@gnzlbg: Для начальной разработки libc ++ было доступно ограниченное количество экономических и временных ресурсов. Впоследствии реализация была обречена не осчастливить каждого пользователя. При наличии доступных ресурсов инженерные компромиссы были сделаны в попытке максимизировать количество довольных пользователей, максимизировать выгоду для всего сообщества C ++. Извините за ваш опыт. Я отмечаю, что расширения кортежей, с которыми вы столкнулись, теперь находятся в текущем рабочем документе C ++ 1z. В этом вопросе вы неосознанно пожертвовали, чтобы многие могли получить выгоду, и многие из них в долгу перед вами.
Ховард Хиннант,