Как стать автором
Обновить

Как я стандартную библиотеку C++11 писал или почему boost такой страшный. Глава 3

Время на прочтение 11 мин
Количество просмотров 12K
Всего голосов 33: ↑33 и ↓0 +33
Комментарии 21

Комментарии 21

универсальность, безусловно, дело хорошее, но столько текста ради константы…
чем дальше, тем с++ все больше становится языком для компиляторов, а не для людей.
Это должно быть скрыто в недрах кода компилятора, а наружу торчать ключевое слово nullptr и тип его. Не берусь судить о «человечности» C++, но столько текста у меня это именно результат:
а) Отсутствия C++ 11 и велосипедостроения.
б) Моей дотошности до соответствия nullptr стандарту.
в) Багов старых компиляторов.

Возможно я немного не в тему, но уже долгое время мучает вопрос. Вы написали, что void * по стандарту должен вмещать в себя любой указатель. Касается ли это указателей на виртуальные методы? И где вообще можно почитать про sizeof указателя на виртуальный метод?

Если кратко то ответ «нет». Указатели на члены класса это отдельные указатели и их нельзя держать в void*. Для этого есть тип указатель-на-член-класса со своим объявлением. У меня речь идет о том что nullptr должен уметь преобразовываться в любой указатель, это обратная операция. По стандарту sizeof(nullptr) == sizeof(void*), потому речь идет именно о указателе void.

sizeof виртуальной функции-члена класса будет в большинстве реализаций больше чем sizeof указателя на void, т.к. по сути обычно это структура, содержащая таблицу адресов виртуальных функций. По стандарту это implementation-defined и разработчики компилятора вольны делать любого размера и представления такие указатели.

Читать — это вам в текст стандарта разве что или доверять выдержкам из него на всяких stack overflow.
Благодарю за ответ. Это может понадобиться, если придётся велосипедить рефлексию/интроспекцию в рамках старых стандартов. Но, как я понимаю, всё равно любой метод (виртуальный или нет) будет представлять из себя обычную «thiscall» функцию. Т.е. имея правильный указатель на класс можно всё равно запихнуть указатель на метод в void*.
На тему того как компактно хранить указатели на свободные функции и функции-члены класса можете взглянуть на fast delegates и понять какой это цирк, а так же на мое расширение всего этого безобразия .

Не понимаю в чем «правильность» может быть у указателя на класс. Указатель на функцию-член класса в void* запихнуть не получится, т.к. у него размер не тот, вы информацию потеряете. А вот хранить связку this_pointer + указатель-на-функцию-член это пожалуйста.
Под правильностью я понимал случаи с полиморфными классами со сложной иерархией наследования. Насколько я понимаю, нельзя «в лоб» получить нужный тип из void * при помощи static_cast при ромбовидном наследовании. Но я не про это.

Допустим, у нас есть виртуальный метод MyClass::Foo( args... );. Соответственно при его вызове неявно первым аргументом передаётся this. Т.е. зная адрес метода в памяти и зная this этот метод можно вызвать как обычную функцию. При этом адрес метода всё равно будет равен разрядности платформы, т.е. sizeof(void*). Это верно?
Но я думаю, что мне лучше не вас в комментариях грузить, а пойти покопать информацию про способы вызова функций. Заранее спасибо.
Не вижу проблемы скастовать к нужному типу. Так что либо я вашу задачу не понимаю, либо я вашу задачу не понимаю.

Это верно, естественно адреса будут в итоге по разрядности платформы. На этот счет коллеги лучше знакомые с ассемблером вам наверняка много чего занимательного расскажут. Верно и то, что неявно передается this как аргумент вызываемой функции. Но только это к языку C++ и типу языка void* уже имеет слабое отношение. Этими вещами занимается компилятор с транслятором, и как они это делают зависит от того как разработчики компилятора это реализовали. Если вас интересует внутреннее представление виртуальных таблиц в разных компиляторах, то я вам ссылку привел на SO выше.

А как покопаете информацию так напишите статью и поделитесь раскопками. Мне будет интересно, уверен сообществу тоже.
Благодарю (голосовать пока не могу). Если будет время — займусь раскопками и статьёй.
Нет. Одна из возможных форм указателя на виртуальный метод — это смещение относительно начала vtable. Плюс нужно где-то хранить тип указателя, чтобы понять на обычный или на виртуальный метод он указывает…
Виртуальный он или нет — не важно, указатель на метод состоит из this и указателя на функцию-имплементацию метода. Если это обычный метод, то указатель на метод будет вычислен во время компиляции, если виртуальный — скопирован из vtable во время исполнения взятия указателя.

Вообще-то нет. this передается уже при вызове и частью указателя не является.


Напомню синтаксис: (foo -> *bar) (x, y) (здесь bar — указатель на метод)


А вот где вы будете искать функцию-имплементацию метода когда она зависит от foo — интересный вопрос...

И я понял что в статье я пропустил часть предложения, потому вышло неоднозначно как то. Сейчас исправлю, хорошо что вы на это обратили внимание.

С ноткой грусти А вы не пробовали OpenCV под Builder собрать? :)

Нет, надеюсь не придется. Хотя может тут найдутся специалисты и из этой области.
Прошу прощения, не силен в C++, но вот это все нужно чтобы просто реализовать null?
Это все нужно чтобы реализовать nullptr на старой версии языка, где его нету.
Если вы про null из C# то да, он является аналогом nullptr из C++ (разве что к bool преобразовываться не умеет).
А на счет «просто реализовать» могу сказать что все это нужно чтобы сложно реализовать своими силами то, что должно быть просто реализовано по более новой редакции стандарта языка силами компилятора.
Я думал что SFINAE это какая-то продвинутая фича, если есть она то есть и nullptr. Но оказывается я ошибался. Довольно хардкорно выглядит
Принцип SFINAE работает не только для перегрузок функций, но и для шаблонов. Но здесь именно перегрузки функций работая как маркеры «да\нет» делают всю работу, потому и описал на этом примере.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории