Почему С++ 11 constexpr настолько ограничительный?
Как вы, наверное, знаете, С++ 11 представляет ключевое слово constexpr
.
С++ 11 вводит ключевое слово constexpr, которое позволяет пользователю гарантировать, что конструктор функции или объекта является временем компиляции постоянная. [...] Это позволяет компилятору понять и проверить, что [имя функции] является константа времени компиляции.
Мой вопрос: почему существуют такие строгие ограничения на форму функций, которые могут быть объявлены. Я понимаю желание гарантировать, что функция чиста, но учтите следующее:
Использование constexpr для функции накладывает некоторые ограничения на то, что эта функция может сделать. Во-первых, функция должна иметь непустой возврат тип. Во-вторых, тело функции не может объявлять переменные или определять новые типы. В-третьих, тело может содержать только объявления, нулевые утверждения и один оператор возврата. Должны существовать значения аргументов, такие как что после замены аргумента выражение в возврате оператор выражает постоянное выражение.
Это означает, что эта чистая функция является незаконной:
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, является чистым?
Ответы
Ответ 1
Причина, по которой вам нужно писать операторы вместо выражений, заключается в том, что вы хотите использовать дополнительные возможности операторов, в частности способность к циклу. Но чтобы быть полезным, для этого потребовалась бы возможность объявлять переменные (также запрещенные).
Если вы объединяете средство для циклирования с изменяемыми переменными, с логическим разветвлением (как в операторах if
), тогда у вас есть возможность создавать бесконечные циклы. Невозможно определить, будет ли такой цикл завершаться (проблема с остановкой). Таким образом, некоторые источники могут привести к зависанию компилятора.
Используя рекурсивные чистые функции, можно вызвать бесконечную рекурсию, которая может быть показана эквивалентно мощной возможностям циклирования, описанной выше. Однако С++ уже имеет эту проблему во время компиляции - это происходит с расширением шаблона - и поэтому у компиляторов уже должен быть переключатель для "глубины стека шаблона", чтобы они знали, когда отказаться.
Таким образом, ограничения, по-видимому, предназначены для обеспечения того, чтобы эта проблема (определения того, когда компиляция С++ когда-либо закончится) не будет более сложной, чем она есть.
Ответ 2
Правила для функций constexpr
разработаны таким образом, что невозможно написать функцию constexpr
, которая имеет какие-либо побочные эффекты.
Если требуется, чтобы constexpr
не имел побочных эффектов, пользователь не может определить, где/когда он был фактически оценен. Это важно, так как функции constexpr
допускаются как во время компиляции, так и во время выполнения по усмотрению компилятора.
Если побочные эффекты были разрешены, тогда должны быть некоторые правила о порядке, в котором они будут наблюдаться. Это было бы невероятно сложно определить - даже сложнее, чем проблема порядка инициализации static
.
Относительно простой набор правил, гарантирующих свободный доступ к этим функциям, должен требовать, чтобы они были всего лишь одним выражением (с некоторыми дополнительными ограничениями над этим). Сначала это звучит ограниченно, и, как вы отметили, исключает оператор if. Хотя в этом конкретном случае не было бы никаких побочных эффектов, это ввело бы дополнительную сложность в правила и при условии, что вы можете писать одни и те же вещи с помощью тернарного оператора или рекурсивно это не очень большое дело.
n2235 - это статья, которая предложила дополнение constexpr
в С++. В нем обсуждается рациональное для дизайна - соответствующая цитата, похоже, такова из обсуждения деструкторов, но в целом имеет значение:
Причина в том, что константное выражение предназначено для оценки компилятором во время перевода, как и любой другой литерал встроенного типа; в частности нет допускается наблюдаемый побочный эффект.
Интересно, что в документе также упоминается, что в предыдущем предложении предполагалось, что компилятор автоматически выяснил, какие функции были constexpr
без нового ключевого слова, но это оказалось неработоспособным, что, похоже, подтверждает мое предположение о том, что правила были разработаны быть простым.
(Я подозреваю, что в ссылках, цитируемых в статье, будут другие цитаты, но это касается ключевого момента моего аргумента о побочных эффектах)
Ответ 3
На самом деле комитет по стандартизации С++ думает об удалении нескольких из этих ограничений для С++ 14. См. Следующий рабочий документ http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3597.html
Ответ 4
Ограничения, безусловно, могут быть отменены совсем немного, без включения кода, который не может быть выполнен во время компиляции или который не может быть доказан, чтобы всегда останавливаться. Однако, я думаю, это не было сделано, потому что
-
это усложнит компилятор для минимального усиления. Компиляторы С++ довольно сложны, как и
-
точное определение того, сколько разрешено без нарушения вышеприведенных ограничений, потребовало бы много времени и учитывая, что желаемые функции были отложены, чтобы получить стандарт вне дома, вероятно, было мало стимулов для добавления большего количества работа (и дальнейшая задержка в стандарте) для небольшого выигрыша.
-
некоторые из ограничений были бы либо довольно произвольными, либо довольно сложными (особенно в циклах, учитывая, что С++ не имеет понятия собственного приращения для цикла, но как конечное условие, так и код приращения имеют для явного указания в инструкции for, что позволяет использовать для них произвольные выражения)
Конечно, только член комитета по стандартам может дать авторитетный ответ, правильно ли допущены мои предположения.
Ответ 5
Я думаю, что constexpr предназначен только для объектов const. Я имею в виду; теперь вы можете статически создавать объекты const, такие как String::empty_string
, статически (без взлома!). Это может сократить время до вызова "main". И статические объекты const могут иметь такие функции, как .length(), operator==,...
, поэтому поэтому требуется "expr". В 'C' вы можете создавать статические константные структуры, как показано ниже:
static const Foos foo = { .a = 1, .b = 2, };
В ядре Linux есть тонны классов этого типа. В С++ вы можете сделать это сейчас с помощью constexpr.
note: Я не знаю, но код ниже не должен приниматься так, как если бы версия:
constexpr int maybeInCppC1Y(int a, int b) { return (a > 0) ? (a + b) : (a - b); }