Почему C ++ 11 constexpr такой строгий? - PullRequest
52 голосов
/ 29 ноября 2011

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

C ++ 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 чиста?

Ответы [ 5 ]

28 голосов
/ 29 ноября 2011

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

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

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

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

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

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

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

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

28 голосов
/ 29 ноября 2011

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

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

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

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

12 голосов
/ 18 июля 2013

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

3 голосов
/ 29 ноября 2011

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

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

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

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

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

0 голосов
/ 01 февраля 2013

Я думаю, что constexpr только для константных объектов.Я имею в виду;теперь вы можете иметь статические константные объекты, такие как String::empty_string, статически (без взлома!).Это может сократить время до вызова «main».А статические константные объекты могут иметь такие функции, как .length(), operator==,..., поэтому необходимо использовать expr.В 'C' вы можете создавать структуры статических констант, как показано ниже:

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); }
...