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

Насколько я могу сказать, функция ниже не constexpr, но код компилируется в clang и g++. Что мне не хватает?

Я получил этот пример из §5.19/2 в N4140:

constexpr int incr(int &n) {
    return ++n;
}

Насколько я могу судить, это не функция constexpr. Но фрагмент компилируется в clang и g++. См. живой пример. Что мне здесь не хватает?

4b9b3361

Ответ 1

В С++ 14 правила для функции constexpr были ослаблены, а бумага N3597: Расслабляющие ограничения на функции constexpr. В статье приводятся обоснования и эффекты, и он включает в себя следующее (основное внимание):

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

и

Несколько синтаксических ограничений на функции constexpr сохраняется:

  • asm-декларации не разрешены.
  • try-blocks и функции-try-blocks не разрешены.
  • Объявления переменных со статикой и длительностью хранения потоков имеют некоторые ограничения (см. ниже).

и мы можем найти это в разделе N4140 7.1.5 [dcl.constexpr], в котором говорится:

Определение функции constexpr должно удовлетворять следующим ограничениям:

  • он не должен быть виртуальным (10.3);

  • его тип возврата должен быть литеральным типом;

  • каждый из его типов параметров должен быть литеральным типом;

  • его тело-функция должно быть = delete, = default или составной оператор, который не содержит

    • определение asm,

    • инструкция goto,

    • блок try или

    • определение переменной нелитерального типа или статической или длительности хранения потоков или для которой инициализация не выполняется.

В последнем примере показано, как incr можно использовать в constexpr:

constexpr int h(int k) {
  int x = incr(k); // OK: incr(k) is not required to be a core
                   // constant expression
  return x;
}

constexpr int y = h(1); // OK: initializes y with the value 2
                        // h(1) is a core constant expression because
                        // the lifetime of k begins inside h(1)

и правило, которое охватывает the lifetime of k begins inside h(1), равно:

  • модификация объекта (5.17, 5.2.6, 5.3.2), если он не применяется к нелетучим значениям lvalue типа literal это относится к энергонезависимому объекту, время жизни которого началось с оценки e;

В тексте 7.1.5 [dcl.constexpr] показано, почему incr является допустимым constexpr:

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

В качестве модифицированного примера, представленного T.C.:

constexpr int& as_lvalue(int&& i){ return i; }

constexpr int x = incr(as_lvalue(1)) ;

мы действительно можем использовать incr как подвыражение основного константного выражения, и поэтому он не плохо сформирован.

Ответ 2

Насколько я могу судить, это не функция constexpr.

Почему ты так говоришь? Пример из §5.19/2 гласит:

constexpr int g(int k) {
    constexpr int x = incr(k); // error: incr(k) is not a core constant
                               // expression because lifetime of k
                               // began outside the expression incr(k)
    return x;
}

incr(k), не являющееся основным константным выражением, не означает, что incr не может быть функцией constexpr.

В соответствии с правилами С++ 14 constexpr можно использовать incr в контексте constexpr, например:

constexpr int incr(int& n) {
    return ++n;
}

constexpr int foo() {
    int n = 0;
    incr(n);
    return n;
}

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

Функция constexpr может содержать даже пути/ветки в теле, которые не были бы constexpr. Пока они никогда не принимаются в контексте constexpr, вы не получите ошибку. Например:

constexpr int maybe_constexpr(bool choice, const int& a, const int& b) {
    return choice ? a : b;
}

constexpr int a = 0;
int b = 1;
static_assert(maybe_constexpr(true, a, b) == 0, "!");

живой пример