Разрешено ли компилятору вызывать немедленную (составную) функцию во время выполнения?
Это может быть глупый вопрос, но я сбит с толку. Было ощущение, что сразу (consteval
) Функция имеет быть выполнена во время компиляции, и мы просто не можем увидеть его тело в двоичном виде.
Эта статья явно подтверждает мое мнение:
Это подразумевает, что [немедленная] функция видна только во время компиляции. Для функции не используются символы, вы не можете получить адрес такой функции, а такие инструменты, как отладчики, не смогут их отобразить. В этом отношении непосредственные функции аналогичны макросам.
Аналогичное сильное утверждение можно найти в публикации Херба Саттера:
Обратите внимание, что черновик C++20 уже содержит часть первого раунда работы, связанной с отражением, чтобы попасть в стандарт: функции consteval , которые гарантированно запускаются во время компиляции, которые были получены в результате работы по отражению и разработаны специально для использования для управлять информацией об отражении.
Однако есть ряд свидетельств, которые не так однозначны в отношении этого факта.
Из cppreference:
consteval - указывает, что функция является немедленной функцией, то есть каждый вызов функции должен производить константу времени компиляции.
Это не означает, что он должен вызываться только во время компиляции.
В настоящее время существует общее мнение, что в будущей языковой поддержке для отражения должны использоваться функции constexpr, но поскольку "функции отражения" обычно должны оцениваться во время компиляции, они, скорее всего, будут немедленными функциями.
Похоже, это означает то, что я думаю, но все же это четко не сказано. Из того же предложения:
Однако иногда мы хотим выразить, что функция всегда должна генерировать константу при вызове (прямо или косвенно), а непостоянный результат должен приводить к ошибке.
Опять же, это не означает, что функция должна оцениваться только во время компиляции.
Из этого ответа:
ваш код должен создавать выражение константы времени компиляции. Но выражение константы времени компиляции не является наблюдаемым свойством в контексте, в котором вы его использовали, и нет никаких побочных эффектов для его выполнения при ссылке или даже во время выполнения! И под как-будто этому ничего не мешает
Наконец, есть живая демонстрация, гдеconsteval
функция явно вызывается во время выполнения. Однако я надеюсь, что это связано с тем, чтоconsteval
еще не поддерживается должным образом в clang, и поведение на самом деле неверно, как и в разделе Почему функция consteval допускает неопределенное поведение?
Точнее, хотелось бы услышать, какие из следующих утверждений процитированной статьи верны:
- Непосредственная функция видна только во время компиляции (и не может быть оценена во время выполнения)
- Символы не выдаются для немедленной функции
- Такие инструменты, как отладчики, не смогут показать немедленную функцию
2 ответа
Точнее, хотелось бы услышать, какие из следующих утверждений процитированной статьи верны:
- Непосредственная функция видна только во время компиляции (и не может быть оценена во время выполнения)
- Символы не выдаются для немедленной функции
- Такие инструменты, как отладчики, не смогут показать немедленную функцию
Практически ни один из этих ответов не может дать стандарт C++. Стандарт не определяет "символы" или то, что могут показывать инструменты. Что касается стандарта, то почти все они выбираются дилером.
В самом деле, даже вопрос о "времени компиляции" и "времени выполнения" - это то, чем стандарт не занимается. Единственный вопрос, который касается стандарта, - это постоянное выражение. Вызовconstexpr
функция может производить постоянное выражение в зависимости от своих параметров. Вызовconsteval
функция, которая не дает постоянного выражения, неправильно сформирована.
Стандарт действительно определяет то, что "видно". Хотя на самом деле дело не во "времени компиляции". В текущем проекте C++20 есть ряд операторов, которые запрещают большинству функций работать с указателями / ссылками на непосредственные функции. Например, N4835 (самый последний рабочий проект C++20) указывает в [expr.prim.id]/3:
Идентификационное выражение, обозначающее немедленную функцию, должно появляться только
как подвыражение немедленного вызова, или
в контексте немедленной функции.
Поэтому, если вы не находитесь в непосредственной функции или не используете имя немедленной функции для вызова другой немедленной функции (передавая указатель / ссылку на функцию), вы не можете назвать немедленную функцию. И вы не можете получить указатель / ссылку на функцию, не назвав ее.
Этот и другие утверждения в спецификации (например, указатели на немедленную функцию, не являющиеся действительными результатами константных выражений) по существу делают невозможным утечку указателя / ссылки на немедленную функцию за пределами константных выражений.
Так что утверждения о видимости непосредственных функций в некоторой степени верны. Символы могут генерироваться для немедленных функций, но вы не можете использовать немедленные функции таким образом, чтобы помешать реализации отбросить указанные символы.
И в этом вся суть consteval
. Он не использует стандартный язык, чтобы навязать то, что должно произойти. Он использует стандартный язык, чтобы сделать невозможным использование функции способом, который предотвратит подобные вещи. Так что разумнее сказать:
Вы не можете использовать немедленную функцию таким образом, чтобы предотвратить ее выполнение компилятором во время компиляции.
Вы не можете использовать немедленную функцию таким образом, чтобы компилятор не отбрасывал для нее символы.
Вы не можете использовать немедленную функцию таким образом, чтобы отладчики могли их видеть.
Ожидается, что качество реализации будет зависеть от этого.
Также следует отметить, что отладочные сборки предназначены для... отладки. Было бы вполне разумно, чтобы продвинутые инструменты компилятора могли отлаживать код, который генерирует постоянные выражения. Так что отладчик, который может видеть немедленное выполнение функций, является совершенно желательной технологией. Это становится все более сложным по мере того, как код времени компиляции становится более сложным.
В предложении упоминается:
Одним из следствий этой спецификации является то, что непосредственная функция никогда не должна быть видна серверной части.
Таким образом, определенно намерение предложения состоит в том, чтобы заменять вызовы константой. Другими словами, постоянное выражение оценивается во время перевода.
Однако это не говорит о том, что это необходимо, чтобы сервер не видел его. Фактически, в другом предложении предложения просто говорится, что это маловероятно:
Это также означает, что, в отличие от простого
constexpr
функции,consteval
функции вряд ли будут отображаться в символических отладчиках.
В более общем плане мы можем переформулировать вопрос следующим образом:
Вынуждены ли компиляторы вычислять константные выражения (везде, а не только тогда, когда они определенно нуждаются в этом)?
Например, компилятору необходимо оценить постоянное выражение, если это количество элементов массива, потому что ему необходимо статически определить общий размер массива.
Однако компилятору может не потребоваться оценивать другие варианты использования, и хотя любой достойный оптимизирующий компилятор все равно попытается это сделать, это не означает, что это необходимо.
Еще один интересный случай, о котором стоит подумать, - это интерпретатор: хотя интерпретатору все еще нужно вычислять некоторые константные выражения, он может просто делать это все время лениво, не выполняя никакого сворачивания констант.
Итак, насколько мне известно, они не требуются, но я не знаю точных цитат, которые нам нужны из стандарта, чтобы доказать это (или иначе). Возможно, это хороший дополнительный вопрос сам по себе, который ответит и на этот.
Например, в [expr.const]p1 есть примечание, в котором говорится, что они могут, а не что они:
[Примечание: постоянные выражения можно оценивать во время перевода. â € ”конец примечания]