#c #templates
#c #шаблоны
Вопрос:
Действительно ли стандарт C предписывает, что экземпляры шаблонов должны создаваться во время компиляции, а не во время выполнения?
Если нет, то является ли это соглашением, которое мы используем исключительно потому, что, очевидно, имеет смысл сделать это таким образом? Или есть какая-то практическая причина, которая теоретически предотвратила бы существование реализации, которая может создавать экземпляры шаблонов во время выполнения?
Комментарии:
1. Гипотетически говоря, как можно создать экземпляр шаблона во время выполнения? Все параметры шаблона фиксированы известны во время компиляции. Если возникают ошибки компилятора из-за создания экземпляра шаблона, они должны быть обнаружены во время компиляции!
2. @Oli: Вопрос в том, предписывает ли стандарт, что «все параметры шаблона фиксированы известны во время компиляции»? Глава и стих?
3. @Tomalak: Если я вызову
my_func_template<T>(...)
, что ещеT
будет, кроме того, что исправлено во время компиляции?4. @Oli: Ничего. Это не цитируемый мандат, согласно которому создание экземпляра должно происходить во время компиляции… только то, что он может (и, конечно, делает).
5. @Tomalak: Тогда ваш вопрос действительно более общий: «Можно ли интерпретировать C во время выполнения?».
Ответ №1:
Все, что требует стандарт, это чтобы наблюдаемое поведение было таким, как если бы шаблоны были созданы до запуска программы. Любые ошибки, например, должны были бы вызвать сообщение на каком-то этапе компиляции или, по крайней мере, перед выполнением. Теоретически компилятор, вероятно, мог бы отложить полное создание экземпляра до времени выполнения, но на практике ему все равно пришлось бы выполнить большую часть работы во время компиляции, чтобы быть уверенным в появлении любых потенциальных сообщений об ошибках.
В более строгом смысле стандарт рассматривает «перевод» как единицу; реализация могла бы, и реализации имеют (и я думаю, что некоторые все еще делают) отложить создание экземпляра до времени соединения. Что приводит к интересным вопросам, когда задействовано динамическое связывание. Но в стандарте об этом ничего не говорится: динамическое связывание — это действительно неопределенное поведение, насколько это касается стандарта, так что это зависит от реализации.
В конце концов, однако: создание экземпляров шаблонов является одной из самых дорогостоящих операций, выполняемых компилятором, и требует очень большого и сложного механизма. Которые ни один поставщик не хочет навязывать исполняемому файлу. Итак, независимо от лазеек, не ожидайте увидеть создание экземпляра во время выполнения в ближайшее время. Тем более, что это все равно ничего вам не даст: стандарт требует, чтобы все шаблоны могли быть созданы во время компиляции, поэтому вы не могли бы создать экземпляр шаблона, каким-либо образом зависящий от аргумента времени выполнения, и при этом соответствовать стандарту.
Комментарии:
1. «не ожидайте увидеть создание экземпляра во время компиляции»
s/compile/run/
?2. Хороший ответ, спасибо. Не могли бы вы вставить туда несколько цитат?
3. @Tomalak Цитировать сложно, потому что это распространено и делается очень косвенно: примером могут служить ограничения на то, что аргументы, не относящиеся к типу, являются константами времени компиляции: даже при создании экземпляра во время выполнения аргумент для
template<int i> class C{};
должен быть, например, целым постоянным выражением; если это переменная, компилятор должен выдать диагностику.4. @Tomalak Программа, которая явно загружает DLL, имеет возможность использовать
system()
для вызова компилятора для их генерации из исходного кода. Однако я не знаю, будет ли это считаться созданием экземпляров шаблонов во время выполнения, которые находятся в исходном коде.:-)5. @JamesKanze, ты про себя подразумевал да первому комментарию LightnessRacesinOrbit?
Ответ №2:
Вы не можете создавать типы в программе на C во время выполнения (пока она выполняется); все они известны во время компиляции. Даже динамически загружаемые разделяемые библиотеки этого не меняют; набор типов в библиотеке известен во время компиляции (когда библиотека скомпилирована), и программа загрузки должна быть способна обрабатывать типы, предоставляемые библиотекой.
Таким образом, нет необходимости в оценке шаблона во время выполнения; вся информация известна во время компиляции.
Если бы вы должны были создавать экземпляры шаблонов во время выполнения, вам понадобились бы компилятор и службы компоновщика как часть времени выполнения. Это значительно усложняет требуемую среду выполнения — без очевидного преимущества.
Очевидно, что интерпретирующая система C могла бы, вероятно, отложила бы создание экземпляра шаблона до необходимой обработки JIT (точно в срок). Но компиляция все равно выполняется до выполнения кода.
Комментарии:
1. Это явно сказано в самом стандарте? Или это просто что-то, что имеет смысл, и не требует явного указания в стандарте? (Конечно, я понимаю, как работают шаблоны; мне просто интересно, насколько их работа конкретно регламентирована спецификацией C , а сколько просто обусловлено здравым смыслом.)
2. Я на это не куплюсь explanation.int известно во время компиляции, но экземпляр объекта создается во время выполнения.
3. @Tomalak: В стандарте C явно не указано, как должна выполняться компиляция, или как собираются программы, или когда происходит множество вещей. Это оставляет эти проблемы для реализации. Это просто определяет требуемое поведение рабочей программы.
4. @John: отдельные объекты создаются во время выполнения, но тип объектов, которые могут быть созданы, известен во время компиляции, даже если вы используете метод заводского конструктора. В конечном счете, все типы в программе известны во время компиляции.
5. GNU предполагает, что компоновщик может когда-нибудь повторно вызвать компилятор с сохраненным состоянием для создания экземпляра. Читать gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html
Ответ №3:
У меня нет привычки читать стандарт, но язык программирования Страуструпа C полезен и точен.
A.9 Шаблоны: предоставляет грамматику для объявления шаблона; параметр типа является одним из class, typename или другого шаблона — таким образом, тип известен, статичен. (это не динамично. можно было бы представить предоставление объекта typeinfo, если бы C был динамическим языком. Но это был не ваш вопрос.)
C.13.8.3 Точка привязки создания экземпляра: указывает, что точка создания экземпляра для шаблона находится непосредственно перед объявлением, использующим его.
Приведенный пример касается преобразования имен в определении шаблона в правильную область видимости. Это было бы очень сложно сделать во время выполнения!
например, из Stroustrup C.13.8.3:
template<class T> void f(T a) { g(a); }
void g(int);
void h()
{
extern g(double);
f(2);
}
«Здесь точка создания экземпляра для f находится непосредственно перед h() , поэтому g (), вызываемый в f (), является глобальным g (int), а не локальным g (double)».
Я предполагаю, что это не исключает JIT, но с практической точки зрения создание экземпляра шаблона f требует знания правильного разрешения g в определенной строке. Как говорит Джонатан, вам понадобятся службы компилятора как часть времени выполнения, наряду с полным контекстом, созданным в компиляторе при компиляции этого модуля. Если это не задание времени компиляции, я не знаю, что это такое.
РЕДАКТИРОВАТЬ: Все это основано на устаревшей версии стандарта C .
Комментарии:
1. Такие не зависящие имена, как
g
, просматриваются во время определения шаблона и должны быть видны тогда: ваш фрагмент кода корректно завершает компиляцию в Comeau с «идентификатором «g» не определен». (Вам также нужен возвращаемый тип дляextern g(double)
КСТАТИ.) Если подумать, это единственный разумный способ, поскольку, если бы он просматривал эти имена во время создания экземпляра, то выполняемый код зависел бы от того, какие объявления были в области видимости перед вызовом, что может сильно различаться, что делает создание экземпляра шаблона очень хрупким.2. Не имеет значения — g — зависимое имя (зависит от T), поэтому оно просматривается во время создания экземпляра. Кстати, фрагмент взят из Страуструпа, 2000, и, за исключением отсутствующего возвращаемого типа, мой g 4.1.2 компилирует его. До сих пор я не знал о Comeau (приятно иметь), но я не думаю, что они правильные.
3. У Comeau есть собственная опция для создания экземпляра шаблона там, где он определен. Это делает поведение Comeau нестандартным. Смотрите Страуструп С.13.8.2, «[в определении] Если имя является зависимым, поиск его определения откладывается до создания экземпляра».
4. Вы правы,
g
это зависимое имя. Но, согласно моему прочтению 14.6.4 / 1 в стандарте 2003, вы (и g , и Comeau w / o -tused, и даже Страуструп!) ошибаетесь относительно того, является ли это законным кодом: «При разрешении зависимых имен учитываются имена из следующих источников: Объявления, которые видны в точке определения шаблона; Объявления из пространств имен, связанных с типами аргументов функции как из контекста создания, так и из контекста определения.»g
не объявлено ранее к определению, иint
не имеет связанных пространств имен.5. Похоже, что именно эта проблема возникла здесь: gcc.gnu.org/bugzilla/show_bug.cgi?id=23885#c6 Разработчик Intel пришел к тому же выводу, что и я — что код не должен компилироваться. (Кстати, тот факт, что книга Страуструпа вышла в 2000 году, проясняет ситуацию — стандарт появился только в 2003 году.) Ты не любишь C ? 😛