Почему не std:: initializer_list - встроенный язык?
Почему нет std::initializer_list
встроенного ядра?
Мне кажется, что это довольно важная особенность С++ 11, но у нее нет собственного зарезервированного ключевого слова (или чего-то подобного).
Вместо этого initializer_list
это просто класс шаблона из стандартной библиотеки, который имеет специальное неявное сопоставление из нового синтаксиса braced-init-list {...}
, который обрабатывается компилятором.
Поначалу это решение довольно хаки.
Является ли это тем, как теперь будут реализованы новые дополнения к языку С++: неявные роли для некоторых классов шаблонов, а не языком core?
Пожалуйста, рассмотрите следующие примеры:
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)
- классический массив, вы могли бы добавить
const
здесь и там
- в языке уже существуют три точки (var-args, теперь variadic templates), почему бы не повторить использование синтаксиса (и заставить его чувствовать себя встроенным).
- только существующий контейнер, можно добавить
const
и &
Все они уже являются частью языка. Я только написал свои 3 первые идеи, я уверен, что есть many другие подходы.
Ответы
Ответ 1
Уже были примеры "основных" языковых функций, возвращающих типы, определенные в пространстве имен std
. typeid
возвращает std::type_info
и (возможно, растягивая точку) sizeof
возвращает std::size_t
.
В первом случае вам необходимо включить стандартный заголовок, чтобы использовать эту так называемую функцию "основного языка".
Теперь, для списков инициализаторов, для генерации объекта не требуется ключевое слово, синтаксис - это зависящие от контекста фигурные скобки. Кроме того, это то же самое, что и type_info
. Лично я не думаю, что отсутствие ключевого слова делает его "более взломанным". Чуть более удивительно, возможно, но помните, что цель заключалась в том, чтобы тот же синтаксис синтаксического кодирования, который уже был разрешен для агрегатов.
Итак, вы, вероятно, можете ожидать большего от этого принципа дизайна в будущем:
- Если возникнет больше случаев, когда можно вводить новые функции без новых ключевых слов, то комитет возьмет их.
- Если новые функции требуют сложных типов, то эти типы будут помещаться в
std
, а не как встроенные.
Следовательно:
- Если новая функция требует сложного типа и может быть введена без новых ключевых слов, тогда вы получите то, что у вас здесь, синтаксис "основного языка" без новых ключевых слов и использует типы библиотек от
std
.
По-моему, я думаю, что нет абсолютного разделения на С++ между "основным языком" и стандартными библиотеками. Они разные главы в стандарте, но каждый ссылается на другой, и он всегда был таким.
В С++ 11 существует другой подход, который заключается в том, что lambdas представляет объекты, которые имеют анонимные типы, сгенерированные компилятором. Поскольку у них нет имен, они вообще не находятся в пространстве имен, но не в std
. Однако это не подходит для списков инициализаторов, потому что вы используете имя типа при написании конструктора, который принимает его.
Ответ 2
Комитет по стандартизации С++, похоже, предпочитает не добавлять новые ключевые слова, возможно, потому, что это увеличивает риск нарушения существующего кода (устаревший код может использовать это ключевое слово как имя переменной, класс или что-то еще).
Кроме того, мне кажется, что определение std::initializer_list
в качестве шаблонного контейнера - довольно элегантный выбор: если бы это было ключевое слово, как бы вы получили доступ к его базовому типу? Как бы вы пробовали его? Вам понадобится множество новых операторов, и это просто заставило бы вас вспомнить больше имен и больше ключевых слов, чтобы делать то же самое, что вы можете делать со стандартными контейнерами.
Обработка std::initializer_list
, как и любой другой контейнер, дает вам возможность писать общий код, который работает с любой из этих вещей.
UPDATE:
Тогда зачем вводить новый тип, вместо того, чтобы использовать некоторую комбинацию существующих? (из комментариев)
Во-первых, все остальные контейнеры имеют методы для добавления, удаления и размещения элементов, которые нежелательны для коллекции, сгенерированной компилятором. Единственным исключением является std::array<>
, который обертывает массив C-стиля фиксированного размера и поэтому остается единственным разумным кандидатом.
Однако, поскольку Nicol Bolas правильно указывает в комментариях, другое фундаментальное различие между std::initializer_list
и всеми другими стандартными контейнерами ( включая std::array<>
), состоит в том, что последние имеют семантику значений, а std::initializer_list
имеет ссылочную семантику. Например, копирование std::initializer_list
не приведет к копированию содержащихся в нем элементов.
Более того (еще раз, любезно предоставлено Nicol Bolas), имея специальный контейнер для списков инициализации скобок, позволяет перегружать то, как пользователь выполняет инициализацию.
Ответ 3
Это ничего нового. Например, for (i : some_container)
полагается на существование определенных методов или автономных функций в классе some_container
. С# даже больше полагается на свои библиотеки .NET. На самом деле, я думаю, это довольно элегантное решение, потому что вы можете сделать ваши классы совместимыми с некоторыми языковыми структурами, не усложняя спецификации языка.
Ответ 4
Это действительно ничего нового и сколько указали, эта практика была там на С++ и есть, скажем, на С#.
Андрей Александреску упомянул об этом неплохо: вы можете подумать об этом как о части мнимого "основного" пространства имен, тогда это будет иметь больше смысла.
Итак, это действительно что-то вроде: core::initializer_list
, core::size_t
, core::begin()
, core::end()
и т.д. Это просто неудачное совпадение, что пространство имен std
содержит внутри него некоторые конструкторы основного языка.
Ответ 5
Он не только полностью работает в стандартной библиотеке. Включение в стандартную библиотеку не означает, что компилятор не может играть умные трюки.
Хотя во всех случаях он может быть неспособен, он вполне может сказать: этот тип хорошо известен или простой тип, позволяет игнорировать initializer_list
и просто иметь образ памяти того, что должно быть инициализированным значением.
Другими словами, int i {5};
может быть эквивалентно int i(5);
или int i=5;
или даже intwrapper iw {5};
Где intwrapper
- это простой класс-оболочка над int с тривиальным конструктором, принимающим initializer_list
Ответ 6
Он не является частью основного языка, потому что он может быть полностью реализован в библиотеке, только строка operator new
и operator delete
. Какое преимущество было бы в том, чтобы компиляторы усложнили сборку?