Чи допускає C ++ 11 вектор <const T>?


82

Вимоги до контейнерів змінилися з C ++ 03 на C ++ 11. Хоча C ++ 03 мав загальні вимоги (наприклад, конструктивність копіювання та призначення для вектора), C ​​++ 11 визначає дрібні вимоги щодо кожної операції з контейнером (розділ 23.2).

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

Мені цікаво: чи означає це, що зараз дозволяє стандарт vector<const T>? Я не бачу жодної причини, чому це не повинно - так const Tсамо, як структура з членом const - це тип, який можна конструювати за копією, але не можна призначити - але я, можливо, щось пропустив.

(Частиною того, що змушує мене думати, що я щось міг пропустити, є те, що магістральний gcc аварійно завершує роботу і згорає, якщо ви намагаєтеся створити екземпляр vector<const T>, але це добре з тим, vector<T>де у T є учасник const).

Відповіді:


55

Ні, я вважаю, що вимоги до розподільника говорять, що T може бути "неконстантним, нереференсним типом об'єкта".

Ви не зможете багато чого зробити з вектором постійних об’єктів. І в const vector<T>будь-якому випадку це буде майже те саме.


Через багато років ця швидка і брудна відповідь все ще приваблює коментарі та голоси. Не завжди вгору. :-)

Отже, щоб додати кілька належних посилань:

Щодо стандарту C ++ 03, який я маю на папері, у таблиці 31 у розділі [lib.allocator.requirements] сказано:

T, U any type

Не те щоб будь-який тип насправді працював.

Отже, наступний стандарт, C ++ 11, говорить у близькому проекті в [allocator.requirements], а тепер у таблиці 27:

T, U, C any non-const, non-reference object type

що надзвичайно близьке до того, що я спочатку писав вище з пам’яті. Це також те, про що було питання.

Однак у C ++ 14 ( проект N4296 ) у таблиці 27 тепер сказано:

T, U, C any non-const object type

Можливо тому, що посилання, можливо, все-таки не є типом об'єкта?

А тепер у C ++ 17 ( проект N4659 ) саме в Таблиці 30 сказано:

T, U, C any cv-unqualified object type (6.9)

Тож не тільки constвиключається, але йvolatile . Напевно, старі новини в будь-якому випадку, і лише роз’яснення.


Будь ласка, також перегляньте інформацію Говарда Хіннанта з перших рук , на даний момент прямо нижче


43
Підсумок: Ми не розробляли контейнери, щоб утримувати const T. Хоча я все-таки задумався. І ми прийшли дуже близько до робити це випадково. Наскільки мені відомо, поточною точкою стикання є пара перевантажених addressфункцій-членів у розподілювачі за замовчуванням: Коли T є const, ці дві перевантаження мають однаковий підпис. Найпростішим способом виправити це було б спеціалізація std::allocator<const T>та усунення одного з перевантажень.
Говард Хіннант

4
@ HighCommander4: Я не впевнений. На libc ++ я можу побудувати вектор (навіть непорожній) за допомогою кооперативного розподільника. Я не можу з цим робити нічого іншого (неконстистського). Я не впевнений, що це відповідає вашому визначенню "працює". Я також не впевнений, якщо мимоволі користуюся перевагою продовження. Щоб бути впевненим, мені потрібно було б витратити набагато більше часу на це питання. Я вкладав таку інвестицію вчасно, але це було кілька років тому, і багато часу змінилося тим часом. Якщо це працює, це не за задумом комітету.
Говард Хіннант

1
@Howard: Я не можу придумати жодної технічної перешкоди для того, щоб зробити це push_back. Але якщо це не дозволено дизайном, нам краще не робити цього. Мені було просто цікаво.
HighCommander4

8
Кеш-пам'ять часто є змінним контейнером незмінних об'єктів, і сортований вектор часто є альтернативою карті, тому я не згоден з тим, що вектор const-об'єктів мало корисний.
Кріс Олдвуд,

9
Це ганьба. Я використовував std::vector<const T>саме тому, що це дуже схоже на const std::vector<T>, але без негативних наслідків останнього для класу, який його проводить. Насправді std::vector<const T>це саме те, що мені потрібно семантично в більшості випадків, коли я використовую vector. Тепер мені доведеться відмовитись const- разом із надійністю, яку вона забезпечує.
Violet Giraffe

30

Оновлення

Відповідно до прийнятої (і правильної) відповіді я прокоментував у 2011 році:

Підсумок: Ми не розробляли контейнери для зберігання const T. Хоча я все-таки трохи подумав. І ми наблизились до того, щоб зробити це випадково. Наскільки мені відомо, поточною точкою стикання є пара перевантажених addressфункцій-членів у розподілювачі за замовчуванням: Коли Tє const, ці дві перевантаження мають однаковий підпис. Найпростішим способом виправити це було б спеціалізація std::allocator<const T>та усунення одного з перевантажень.

З майбутнім проектом C ++ 17 мені здається, що ми вже легалізовані vector<const T>, і я також вважаю, що ми зробили це випадково . :-)

P0174R0 знімає addressперевантаження з std::allocator<T>. P0174R0 не згадує про підтримку std::allocator<const T>як частину свого обґрунтування.

Виправлення

У коментарях нижче ТК правильно зазначає, що address перевантаження застаріли , а не усували. Моє ліжко. Застарілі члени не відображаються в 20.10.9 там, де std::allocatorце визначено, а замість цього переходять до розділу D.9. Коли я розмістив це, я нехтував просканувати розділ D щодо такої можливості.

Дякуємо TC за виправлення. Я задумав видалити цю оманливу відповідь, але, мабуть, найкраще залишити це з цим виправленням, щоб, можливо, це завадило комусь іншому помилково прочитати специфікацію так само, як і я.


7
Це досить кумедно! (Тепер нам просто потрібно бути по-справжньому тихим, і нехай він проскакує у C ++ 17, не помічаючи цього :))
HighCommander4

Хіба таблиця вимог розподілювача все ще просто не забороняє це прямо? Незважаючи на це, P0174R2 (за перегляд, за який проголосовано) лише знецінює, а не видаляє address.
TC

@TC: Ви абсолютно праві. Дякую за виправлення.
Говард Хіннант

Тож c ++ 2x нарешті дозволить vector<const T>:)
MM

1
Відповідь "Підсумок: Ми не розробляли контейнери для утримання const T." передбачає, що мета полягає в тому, що контейнер повинен містити "const T". Однак можна стверджувати, що метою користувача є обмеження операцій над контейнером - таким чином, наприклад, 'back ()' повертає "const T &" - ​​незалежно від того, що містить контейнер.
Ганс Олссон,

8

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

Отже, це не працює:

vector<const T> vec; 

Просто прочитайте інші відповіді, щоб зрозуміти, чому. І, як ви вже здогадалися, це теж не спрацює:

vector<const shared_ptr<T>> vec;

Tвже немає const, але vectorтримає shared_ptrs, а не Ts.

З іншого боку, це робить роботу:

vector<const T *> vec;
vector<T const *> vec;  // the same as above

Але в цьому випадку const - це об’єкт, на який вказують, а не сам вказівник (саме це зберігає вектор). Це було б еквівалентно:

vector<shared_ptr<const T>> vec;

Що добре.

Але якщо ми ставимо constв кінець виразу, він тепер перетворює покажчик на a const, тому наступне не компілюється:

vector<T * const> vec;

Трохи заплутано, я згоден, але ти звикнеш.


4

Доповнюючи інші відповіді, слід використати інший підхід:

vector<unique_ptr<const T>> vec;

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

Як було відзначено, покажчик constсемантика може бути заплутаним, але shared_ptrі unique_ptrне є. const unique_ptr<T>є покажчиком const і unique_ptr<const T>є показником const, як і слід було очікувати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.