Подтвердить что ты не робот

Почему С++ 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, является чистым?

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

Ответ 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); }