Чому std :: initializer_list не є вбудованою мовою?


95

Чому не std::initializer_listвбудована основна мова?

Мені здається, що це досить важлива особливість C ++ 11, і все ж він не має власного зарезервованого ключового слова (або чогось іншого).

Натомість initializer_listце просто клас шаблону зі стандартної бібліотеки, який має спеціальне, неявне зіставлення з нового синтаксису дужок-init-list, {...} що обробляється компілятором.

На перший погляд, це рішення є досить хакі .

Чи так тепер будуть реалізовані нові доповнення до мови С ++: неявними ролями деяких класів шаблонів, а не основною мовою?


Будь ласка, розгляньте ці приклади:

   widget<int> w = {1,2,3}; //this is how we want to use a class

чому був обраний новий клас:

   widget( std::initializer_list<T> init )

замість того, щоб використовувати щось подібне до будь-якої з цих ідей:

   widget( T[] init, int length )  // (1)
   widget( T... init )             // (2)
   widget( std::vector<T> init )   // (3)
  1. класичний масив, ви можете, напевно, додати constсюди-туди
  2. три крапки вже існують в мові (var-args, тепер варіативні шаблони), чому б не повторно використовувати синтаксис (і зробити так, щоб він відчував себе вбудованим )
  3. просто існуючий контейнер, міг додати constі&

Усі вони вже є частиною мови. Я написав лише свої 3 перші ідеї, я впевнений, що існує багато інших підходів.


26
Комітет зі стандартів ненавидить додавати нові ключові слова!
Алекс Чемберлен,

11
Це я розумію, але є багато можливостей, як розширити мову ( ключове слово було лише прикладом )
emesx

10
std::array<T>не є "частиною мови", ніж std::initializer_list<T>. І це далеко не єдині бібліотечні компоненти, на які покладається мова. Див. new/ delete, type_infoРізні типи винятків size_tтощо
bames53,

6
@Elmes: Я б запропонував const T(*)[N], оскільки це поводиться дуже подібно до того, як це std::initializer_listпрацює.
Mooing Duck

1
Це відповідає, чому std::arrayмасив із статичним розміром є менш бажаними альтернативами.
boycy 07.03.13

Відповіді:


48

Уже були приклади "основних" функцій мови, які повертали типи, визначені у stdпросторі імен. typeidповертається std::type_infoі (можливо, розтягуючи точку) sizeofповертається std::size_t.

У першому випадку вам вже потрібно включити стандартний заголовок, щоб використовувати цю так звану функцію "основної мови".

Зараз для списків ініціалізатора трапляється, що для генерації об’єкта не потрібне ключове слово, синтаксис - це контекстно-залежні фігурні дужки. Крім того, це те саме, що type_info. Особисто я не думаю, що відсутність ключового слова робить його "більш хакерським". Можливо, трохи дивніше, але пам’ятайте, що метою було дозволити той самий синтаксис дужки-ініціалізатора, який вже був дозволений для агрегатів.

Так що так, ви можете, мабуть, очікувати більше цього принципу дизайну в майбутньому:

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

Звідси:

  • якщо нова функція вимагає складного типу і може бути введена без нових ключових слів, тоді ви отримаєте те, що у вас є, тобто синтаксис "основної мови" без нових ключових слів і використовує типи бібліотек з std.

Я думаю, що це зводиться до того, що в C ++ немає абсолютного поділу між "основною мовою" та стандартними бібліотеками. Це різні глави стандарту, але кожна посилається на іншу, і це було завжди.

У C ++ 11 є інший підхід, який полягає в тому, що лямбди вводять об’єкти, які мають анонімні типи, генеровані компілятором. Оскільки вони не мають імен, вони взагалі не знаходяться у просторі імен, звичайно, не в std. Однак це не підходить для списків ініціалізатора, оскільки ви використовуєте ім'я типу, коли пишете конструктор, який приймає такий.


1
Мені здається, що такий розподіл неможливий (mailny?) Через такі неявні ролі типів. type_infoі size_tприємні аргументи .. ну size_tце просто typedef .. так що давайте пропустимо це. Різниця між type_infoта initializer_listполягає в тому, що перший результат явного оператора, а другий - неявної дії компілятора. Мені також здається, що це initializer_list можна замінити деякими вже існуючими контейнерами .. чи ще краще: будь-який користувач оголошує як тип аргументу!
emesx 04.03.13

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

2
@Christian: Ні, std::arrayнавіть не має жодних конструкторів. std::arrayСправа просто агрегатно-ініціалізації. Крім того, я вітаю вас приєднатися до мене у чаті Lounge <C ++>, оскільки це обговорення стає дещо довгим.
Xeo

3
@ChristianRau: Xeo означає, що елементи копіюються при побудові списку ініціалізатора. Копіювання списку ініціалізатора не копіює містяться елементи.
Mooing Duck

2
@Christian List-Initialisation не означає ініціалізатор_list. Це може бути багато речей, включаючи хорошу пряму ініціалізацію оле або сукупну ініціалізацію. Жоден з них не включає Initializer_list (а деякі просто не можуть працювати таким чином).
R. Martinho Fernandes

42

Здається, Стандартний комітет С ++ вважає за краще не додавати нові ключові слова, ймовірно, тому, що це збільшує ризик злому існуючого коду (застарілий код міг використовувати це ключове слово як ім'я змінної, класу чи чогось іншого).

Більше того, мені здається, що визначення std::initializer_listяк шаблонного контейнера є цілком елегантним вибором: якби це було ключове слово, як би ви отримали доступ до його основного типу? Як би ви це повторили? Вам також знадобиться купа нових операторів, і це просто змусить вас запам'ятати більше імен і більше ключових слів, щоб робити те саме, що ви можете робити зі стандартними контейнерами.

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

ОНОВЛЕННЯ:

Тоді навіщо вводити новий тип, замість того, щоб використовувати якусь комбінацію існуючих? (з коментарів)

Почнемо з того, що всі інші контейнери мають методи додавання, видалення та заміни елементів, що не бажано для колекції, створеної компілятором. Єдиним винятком є ​​те std::array<>, що обгортає масив у стилі C фіксованого розміру і, отже, залишається єдиним розумним кандидатом.

Однак, як правильно зазначає Нікол Болас у коментарях, інша, принципова відмінність між std::initializer_listусіма стандартними контейнерами (включаючи std::array<>) полягає в тому, що останні мають семантику значення , тоді як std::initializer_listмають посилальну семантику . std::initializer_listНаприклад, копіювання , не призведе до копії елементів, які він містить.

Більше того (ще раз, люб'язно надано Nicol Bolas), наявність спеціального контейнера для списків для ініціалізації фігурних дужок дозволяє перевантажувати спосіб виконання користувачем ініціалізації.


4
Тоді навіщо вводити новий тип, замість того, щоб використовувати якусь комбінацію існуючих?
emesx 04.03.13

3
@elmes: Насправді це більше схоже std::array. Але std::arrayвиділяє пам'ять під час std::initializaer_listобгортання масиву часу компіляції. Подумайте про це як про різницю між char s[] = "array";і char *s = "initializer_list";.
rodrigo

2
І якщо це нормальний тип, це не викликає проблем із перевантаженням, спеціалізацією на шаблони, оформленням імен тощо.
rodrigo

2
@rodrigo: std::arrayне виділяє жодної пам'яті, це звичайне T arr[N];, те саме, що підтримує std::initializer_list.
Xeo

6
@Xeo: T arr[N] виділяє пам'ять, можливо, не в динамічній купі, а деінде ... Так само std::array. Однак непусте initializer_listне може бути сконструйоване користувачем, тому воно, очевидно, не може розподілити пам'ять.
rodrigo

6

Це нічого нового. Наприклад, for (i : some_container)покладається на існування конкретних методів або автономних функцій у some_containerкласі. C # навіть більше покладається на свої бібліотеки .NET. Насправді, я думаю, що це цілком елегантне рішення, оскільки ви можете зробити свої класи сумісними з деякими мовними структурами, не ускладнюючи мовної специфікації.


2
методи в класі або самостійно beginта endметоди. Це трохи інше IMO.
emesx 04.03.13

3
Є це? Знову ж таки, у вас є чисто мовна конструкція, що спирається на конкретну конструкцію вашого коду. Це також могло бути зроблено шляхом введення нового ключового слова, наприклад,iterable class MyClass { };
Spook

але ви можете розміщувати методи куди завгодно, впроваджувати їх як завгодно .. Є якась схожість, я згоден! Це питання, initializer_listоднак,
emesx

4

Це справді нічого нового, і скільки зазначали, така практика існувала в C ++ і є, скажімо, в C #.

Андрій Александреску згадав хороший момент з цього приводу: ви можете сприймати це як частину уявного "основного" простору імен, тоді це матиме більше сенсу.

Таким чином, це на самому справі що - щось на кшталт: core::initializer_list, core::size_t, core::begin(), core::end()і так далі. Це лише невдалий збіг обставин, що stdвсередині простору імен є основні мовні конструкції.


2

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

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

Іншими словами , int i {5};може бути еквівалентно int i(5);або int i=5;навіть intwrapper iw {5};де intwrapperпростий обгорткою класу над міжнар з тривіальним конструктор приймаєinitializer_list


Чи є у нас відтворювані приклади компіляторів, які насправді грають у такі «розумні трюки»? Це начебто міркує як ніби , але я хотів би бачити обґрунтування.
underscore_d

Ідея оптимізації компіляторів полягає в тому, що компілятор може перетворити код у будь-який еквівалентний код. C ++, зокрема, покладається на оптимізацію для "безкоштовних" абстракцій. Ідея заміни коду зі стандартної бібліотеки є загальною (подивіться на вбудований список gcc gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html ).
Paul de Vrieze

Насправді ваша ідея, яка int i {5}стосується будь-якого, std::initializer_listє неправильною. intне має конструктора std::initializer_list, тому 5просто використовується безпосередньо для його побудови. Тож головний приклад не має значення; просто не потрібно оптимізувати. Окрім цього, оскільки std::initializer_listкомпілятор включає створення та проксі "уявного" масиву, я думаю, це може сприяти оптимізації, але це "магічна" частина в компіляторі, тому це окремо від того, чи може оптимізатор взагалі зробити щось розумне з досить тупий об’єкт, що містить 2 ітератори, результат
underscore_d

1

Це не частина основної мови, оскільки її можна повністю реалізувати в бібліотеці, просто рядок operator newі operator delete. Яка перевага була б у тому, щоб ускладнювати компілятори для її вбудовування?

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