Почему c++11 constexpr настолько ограничительный?



Как вы, наверное, знаете, C++11 вводит constexpr ключевое слово.




C++11 ввел ключевое слово constexpr, которое позволяет пользователю
гарантируйте, что конструктор функции или объекта является временем компиляции
постоянный.
[...]
Это позволяет компилятору понять и проверить, что [имя функции] является
константа времени компиляции.




мой вопрос почему такие строгие ограничения на форму функции, которые могут быть объявлены. Я понимаю желание гарантировать, что функция чиста, но подумайте об этом:




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




это означает, что эта чистая функция является незаконной:



constexpr int maybeInCppC1Y(int a, int b)
{
if (a>0)
return a+b;
else
return a-b;
//can be written as return (a>0) ? (a+b):(a-b); but that isnt the point
}


Также вы не можете определить локальные переменные... :(
Поэтому мне интересно, это дизайнерское решение, или компиляторы сосут, когда дело доходит до доказательства того, что функция A чиста?

582   5  

5 ответов:

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

Если вы объедините средство для циклирования, с изменяемыми переменными, с логическим ветвлением (как в if операторы), ТО у вас есть возможность создавать бесконечные циклы. Невозможно определить, является ли такое цикл когда-либо завершится (проблемы остановки). Таким образом, некоторые источники могут привести к зависанию компилятора.

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

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

правила constexpr функции разработаны таким образом, что это невозможно написать constexpr функция, которая имеет любые побочные эффекты.

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

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

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

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

причина в том, что константа-выражение предназначено для вычисления компилятором во время перевода так же, как и любой другой литерал встроенного типа; в частности нет наблюдаемый побочный эффект разрешен.

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

(Я подозреваю, что в ссылках, приведенных в статье, будут и другие цитаты, но это охватывает ключевой момент моего аргумента об отсутствии побочных эффектов)

на самом деле комитет по стандартизации C++ думает об устранении некоторых из этих ограничений для c++14. См. следующий рабочий документ http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3597.html

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

  • это усложнило бы компилятор для минимального усиления. Компиляторы C++ довольно сложны, как и

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

  • некоторые ограничения были бы либо довольно произвольными, либо довольно сложными (особенно в циклах, учитывая, что C++ не имеет понятия собственного приращения для цикла, но и конечное условие, и код приращения должны быть явно указаны в for оператор, позволяющий использовать для них произвольные выражения)

конечно, только член комитета по стандартам может дать авторитетный ответ, верны ли мои предположения.

Я думаю, что constexpr только для объектов const. Я имею в виду, теперь вы можете иметь статические объекты const как String::empty_string строит статически (без взлома!). Это может сократить время до вызова "main". И статические объекты const могут иметь такие функции, как .length(), operator==,... Так вот почему "expr" необходим. В СИ вы можете создавать статические постоянные структуры, как показано ниже:

static const Foos foo = { .a = 1, .b = 2, };

ядро Linux имеет тонны классов этого типа. В c++ вы можете сделать это сейчас с помощью constexpr.

примечание: Я не знаю, но код ниже не должен быть принят так, как если бы версия:

constexpr int maybeInCppC1Y(int a, int b) { return (a > 0) ? (a + b) : (a - b); }

Comments

    Ничего не найдено.