Чому вектор libc ++ <bool> :: const_reference не є 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>{}, "?");
}

Крім того, я зазначаю, що стандарт С ++ відповідав цій специфікації аж до С ++ 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також може використовувати невелике оновлення в цьому відділі, наприклад, add const_referenceта набір ітераторів.

Тобто, заднім числом bitsetє vector<bool>(що має бути перейменовано на bit_vector- або що завгодно), як arrayі 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. Щодо цього питання, ти несвідомо пожертвував, щоб багато хто міг отримати вигоду, а багато хто винні тобі вдячні.
Говард Хіннант
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.