Почему, если применимо, подразумевается не constexpr?

Вероятно, они должны быть в разных вопросах, но они связаны так...

  • Зачем нам вообще писать constexpr? Учитывая набор ограничений, компилятор не смог оценить код, чтобы убедиться в том, удовлетворяет ли он требованиям constexpr и обрабатывает его как constexpr, если он делает?. В качестве чисто документационного ключевого слова я не уверен, что он содержит потому что я не могу придумать случай, когда мне (пользователю какой-то функции constexpr) по-настоящему нужно, чтобы оно выполнялось или не выполнялось.

    Вот моя логика: если это дорогостоящая функция, я думаю, что в качестве хорошей практики я должен рассматривать ее как таковую, независимо от того, могу ли я давать ей постоянный ввод времени компиляции или нет. Это может означать вызов его во время загрузки и сохранение результата, а не вызов его в критический момент выполнения. Причина в том, что constexpr на самом деле не гарантирует мне, что он не будет выполняться во время выполнения в первую очередь - так, возможно, это должен сделать новый/другой механизм.

  • Ограничения constexpr, похоже, исключают многие, если не большинство, функции из оценки времени компиляции, которые логически могут быть. Я читал это, по крайней мере, частично (или, возможно, полностью?), чтобы предотвратить бесконечный цикл и зависание компилятора. Но, если это причина, это законно?

Не следует ли компилятору вычислить, если для любой заданной функции constexpr с указанными входами она бесконечно петляет? Это не решает проблему остановки для любого ввода. Вход в функцию constexpr - это константа времени компиляции и конечная, поэтому компилятору нужно только проверять бесконечный цикл для конечного набора входных данных: фактически используемый вход. Должна быть регулярная ошибка компиляции, если вы пишете бесконечный цикл времени компиляции.

Ответы

Ответ 1

Я задал очень похожий вопрос: Почему нам нужно отмечать функции как constexpr?

Когда Я нажал Ричард Смит, автор Clang, объяснил:

Ключевое слово constexpr имеет полезность.

Это влияет на создание экземпляра функции шаблона функции (возможно, необходимо создать экземпляр функции шаблона функции constexpr, если они вызваны в неоцененных контекстах, то же самое не относится к функциям non-constexpr, поскольку вызов к одному никогда не может быть частью постоянного выражения). Если мы удалим значение ключевого слова, нам нужно будет создать экземпляр более специализированных функций раньше, на всякий случай, когда вызов будет постоянным выражением.

Это сокращает время компиляции, ограничивая набор вызовов функций, реализация которых требуется для оценки во время перевода. (Это имеет значение для контекстов, где реализации необходимы для проверки оценки постоянных выражений, но это не ошибка, если такая оценка не удалась - в частности, инициализаторы объектов статической продолжительности хранения.)

Вначале все это казалось неразумным, но если вы работаете с деталями, все происходит без constexpr. Функция не должна создаваться, пока не будет использована ODR, что по существу означает использование во время выполнения. Особенность функций constexpr заключается в том, что они могут нарушать это правило и в любом случае требуют создания экземпляра.

Функциональная реализация - это рекурсивная процедура. Выполнение функции приводит к созданию экземпляров функций и классов, которые он использует, независимо от аргументов для любого конкретного вызова.

Если что-то пошло не так, создавая экземпляр этого дерева зависимостей (возможно, при значительных затратах), было бы трудно усвоить ошибку. Кроме того, при создании экземпляра класса могут возникать побочные эффекты времени выполнения.

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

Уверенный в себе угол, но плохие вещи могут произойти, если вы не требуете от людей доступа к функциям constexpr.

Что касается объектов constexpr, некоторые типы могут генерировать основные константные выражения, которые можно использовать в контекстах с постоянным выражением без объявления constexpr. Но вы не хотите, чтобы компилятор попытался оценить каждое выражение во время компиляции. Это то, для чего нужно постоянное распространение. С другой стороны, очень важно документировать, когда что-то должно произойти во время компиляции.

Ответ 2

[Заметьте, я полностью изменил свой ответ]

Чтобы ответить на второй вопрос, для компилятора есть два случая:

  • Компилятор должен иметь возможность обрабатывать любые произвольные функции constexpr. В этом случае у вас все еще есть проблема с остановкой, потому что набор входов - это все комбинации функций constexpr и вызовы к ним.

  • Компилятор может обрабатывать конечный набор функций constexpr. В этом случае компилятор может определить, будут ли некоторые программы приводить к бесконечным циклам, тогда как другие программы будут несовместимы (поскольку они не входят в набор допустимых входов).

Таким образом, предположительно ограничения существуют, так что он удовлетворяет случаю 2 для разумного объема усилий компилятора.

Ответ 3

В основе этого решения лежат как технические, так и идеологические причины.

  • Мы не всегда хотим constexpr по умолчанию - это может быть слишком много времени компиляции. Это первое. Представьте, что вы реализовали isPrime, и у вас есть 100 вызовов с большим constexpr значения передавались. Я думаю, что вы (в большинстве случаев) не хотите компилятор компилирует это на пару минут дольше, потому что он решил, что вам нужны эти значения во время компиляции. Но если это именно так - указать модификатор constexpr вручную. И это добавляет следующий пункт:

  • обратная совместимость - неразумно предполагать, что любой возможный автор программы С++ 98, который преобразовал эту программу в С++ 11, хочет constexpr.

  • Второй момент заключается в том, что решение о том, может ли функция быть constexpr займет время компиляции. И если он пытался это сделать для каждой возможной функции, потребовалось бы дополнительное время. Еще больше, часто компилятор не мог решить, может ли данная функция быть constexpr вообще, поэтому ваше первое предположение неверно.