Я получил этот пример из §5.19/2 в N4140:
constexpr int incr(int &n) {
return ++n;
}
Насколько я могу судить, это не функция constexpr
. Но фрагмент компилируется в clang и g++. См. живой пример. Что мне здесь не хватает?
Я получил этот пример из §5.19/2 в N4140:
constexpr int incr(int &n) {
return ++n;
}
Насколько я могу судить, это не функция constexpr
. Но фрагмент компилируется в clang и g++. См. живой пример. Что мне здесь не хватает?
В С++ 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
как подвыражение основного константного выражения, и поэтому он не плохо сформирован.
Насколько я могу судить, это не функция
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, "!");