ума. Я знаю, что понятия КЛАСС и ТИП тождественны. В С++ мы можем завести пользовательские типы - они же пользовательские классы, так же? ТО есть в моем понимании слово класс и слово тип это синонимы (при этом я понимаю разницу между классом и экземпляром класса: класс это заготовка (чертеж изделия), а экземпляр это реальный объект этого типа (класса). А тут в учебнике еще и встретилось понятие как ТИП КЛАССА! То есть оказывается, у класса (который сам по себе является типом), есть еще и какой то другой тип. Я не понимаю понятие ТИП КЛАССА (Звучит как ТИП ТИПА по сути). Вот выдержка из учебника - " Например,
в коде string : : size_type, компилятор имеет определение класса string
и может узнать, что s i z е _ t уре - это тип. " Для меня это звучит как: у типа String есть тип size_type. Ничего не понимаю. Как понять у типа string (который сам по себе итак тип) есть в загашнике еще какой то свой тип!!!
на каком языке учебник?
Возможно, имелось в виду typeclass?
речь о вложенных классах
template<typename T> struct S{ using value_type = T; }; у структуры S есть тип value_type
НЕ знаю что они имеют ввиду. Вот полный текст. Помните, как в разделах 7.4 (стр. 366) и 7.6 (стр. 388) использовался опера тор области видимости ( : : ) для обращения к статическим членам и членам типа. В обычном коде (не шаблона) у компилятора есть доступ к определению класса. В результате он знает, является ли имя, к которому обращаются через оператор области видимости, типом или статическим членом. Например, в коде string : : size_type, компилятор имеет определение класса string и может узнать, что s i z е _ t уре - это тип. С учетом того, что т является параметром типа шаблона, когда компилятор встретит такой код, как т : : mem, он не будет знать до времени создания экзем пляра, является ли mem типом или статической переменной-членом. Но чтобы обработать шаблон, компилятор должен знать, представляет ли имя тип. На пример, если т является именем параметра типа, то как компилятор воспри мет следующий код: Т : : s ize_type * р ; Он должен знать, определяется ли переменная по имени р или происходит умножение статической переменной-члена по имени size_type на перемен ную по имени р. По умолчанию язык подразумевает, что имя, к которому обращаются че рез оператор области видимости, не является типом. В результате, если необ ходимо использовать тип-член параметра типа шаблона, следует явно указать компилятору, что имя является типом. Для этого используется ключевое слово typename
Да, выше правильно сказали, что это о вложенных классах
А зачем они нужны вложенные классы то? Ведь я так понимаю, если класс вложенный, то я не могу его инстанцировать сам по себе вне класса , в котором описан вложенный тип? Я раньше думал, что в С++ доступны все типы какие определены и я могу создать экземпляр любого класса (типа). А с вложенными непонятно, зачем они, если они ограничены только внутри типа хозяина? Или я неправильно понимаю?
Можно создать экземпляр вложенного класса/типа и без создания внешнего. Например, std::string::size_type variable
Тогда тем более непонятно стало в чем смысл вложенных классов. Ну был бы этот тип не вложенным и что было бы?
Удобнее организовывать код
Не было бы явно выраженной связи. Тут точно понятно, что это тип размера для строчки. Был бы он отдельно, чем бы был этот тип?
То есть можно создать несколько типов с одинаковым названием внутри разных типов? Что то типа namaspace что ли это ?
если ты не знаешь про шаблоны и про using то тебе рано разбираться зачем это нужно. По факту это часть интерфейса класса, как бы это сказать... Контейнер без iterator(который объявлен внутри) - не контейнер.
и можно использовать любые типы, они такие же как и те что снаружи
Ты хочешь сказать, что на примере итераторов (которые как ты говоришь являются вложенными в контейнер типами), есть некая договоренность, что вот если есть контейнер, то в нем обязательно должен быть вложенный тип под названием iterator? И так во всех контейнерах свои вложенные итераторы? Почему бы тогда бы не договориться было, что бы был один тип итератор (не вложенный, а просто тип итератор), который бы мог использоваться любым контейнером? Не понимаю зачем внутри каждого типа контейнера свои вложенный тип итератор...
потому что 1. непонятно как же сделать такой итератор, допустим у меня итератор это просто T*, тогда я должен делать специализацию стандартного iterator и заставлять компилятор генерировать лишнюю структуру? 2. это значительно менее гибко(итераторов может быть много, обычный const итератор на end() отдельным типом и т.д.) 3. это заставляет подключать дополнительную либу типо #include <iterator> 4. это заставило бы компилятор каждый раз выбирать из огромной кучи итераторов 5. это повысило бы нужную компетенцию программиста для написания контейнера 6. это было бы некрасиво 7. Главная причина - эти "вложенные типы" используются вот так *какой то шаблонный код" template<typename Container, typename = std::void_t<typename Container::value_type>> bool is_good(Container&& c) // проверка что у контейнера вообще есть value_type или if constexpr( std::is_same_v<typename Container::value_type, int>) или(самый хороший пример) проверка iterator_category внутри всяческих алгоритмов для их оптимизации Короче нужны именно типы, которые нёс бы за собой сам тип контейнера. Чтобы по его типу можно было узнать о нём всё что нужно. А твой <iterator> имитируется std::iterator_traits
Оберните код в теги: 3 символа ` до и после кода (в случае одиночной конструкции достаточно 1 ` с обеих сторон). Спасибо!
Да все правильно ты понимаешь.
Но тут же string::size_type это другой тип, отличный от string, и вложенный в него.
В учебнике все верно, читай внимательно
Так эти типы связаны друг с другом, и внешний тип служит пространством имён для вложенного, его уже не перепутать с другим типом size_type от другого класса , скажем, stringg string::size_type и stringg::size_type - разные вещи. К тому же string::size_type может быть в приватной области класса string, тогда его снаружи вообще не видно
Примерно понятно, Однако,поясни, пожалуйста еще вот что. Вот смотри, в моем понимании класс инкапсулирует в себя данные и предоставляет рычаги (методы) для обслуживания данных внутри себя. В случае же с итераторами, получается контейнер вместо того, чтобы предоставить метод, который бы как итератор отдавал наружу данные, (есть же наконец геттеры!), так вот контейнер держит внутри себя вложенный класс -итератор. Получается такая схема - шаблонный класс контейнер- внутри него вложенный класс итератор - внутри итератора методы (оператор [] как вариант). Вот я и не могу понять зачем такая сильная вложенность, почему сразу не предоставить контейнеру метод, который бы и работал как итератор и отдавал данные наружу, вместо того, чтоб делегировать это работу методу вложенного класса-итератора? Вот это для меня непонятно, в чем тут фишка?
Потому что итераторы разных контейнеров разные. Разные типы, разные у них размеры, разные свойства, разные данные внутри.
Слушай, контейнеры STL - это отдельная, и не всегда радостная песня. Надо начать с того, что их придумали вообще для другого языка программирования. А потом перенесли на С++, причём в С++ для этого появились шаблоны.
Ну и предоставлял бы каждый контейнер методы для доступа к своим данным (геттеры), а они отдают эту работу методам вложенного класса-итератора
Я к тому, что не надо пытаться изучать С++ по контейнерам STL.
Я принцип хочу понять. Как работать с итераторами я знаю. Я хочу понять допустим, как свой итератор сделать и почему они реализованы именно как вложенные классы, а не как просто методы контейнера.
Ну просто ПОТОМУ ЧТО. Вложенные — потому что тогда не было namespace -ов. Не как методы — потому что Степанов хотел (ну и добился этого частично) сделать общие способы работы с контейнерами разных типов.
и как ты себе это представляешь, контейнер везде таскать и просить у него next элемент?
Надо осмыслить. К сожалению надо ехать на работу. Почитаю еще про шаблоны. Более менее понятно, спасибо всем!
Ещё раз, не надо учить шаблоны на базе контейнеров STL
Обсуждают сегодня