据我所知,下面的函数不是 constexpr,而是用 clang 和 g++ 编译的代码。我错过了什么?
As far as I can tell the function below is not constexpr, but the code compiles in clang and g++. What am I missing?
我从 N4140 的 §5.19/2 中得到这个例子:
constexpr int incr(int &n) {
return ++n;
}
据我所知,这不是 constexpr
函数。但是这段代码是用 clang 和 g++ 编译的。参见 live example。我在这里错过了什么?
As far as I can tell, this is not a constexpr
function.
为什么这么说?
§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 函数。
根据 C++14 的 constexpr 规则,可以在 constexpr 上下文中使用 incr
,例如:
constexpr int incr(int& n) {
return ++n;
}
constexpr int foo() {
int n = 0;
incr(n);
return n;
}
除非函数体完全不可能是 constexpr
(例如,无条件调用非 constexpr 函数),否则编译器没有理由在定义点产生错误。
constexpr 函数甚至可以在主体中包含 paths/branches 而不是 constexpr。只要它们从未在 constexpr context 中使用,您就不会收到错误。
例如:
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, "!");
在 C++14 中,constexpr 函数的规则放宽了,论文 N3597: Relaxing constraints on constexpr functions。该论文探讨了基本原理和效果,包括以下内容(强调我的):
As in C++11, the constexpr keyword is used to mark functions which the implementation is required to evaluate during translation, if they are used from a context where a constant expression is required. Any valid C++ code is permitted in constexpr functions, including the creation and modification of local variables, and almost all statements, with the restriction that it must be possible for a constexpr function to be used from within a constant expression. A constant expression may still have side-effects which are local to the evaluation and its result.
和:
A handful of syntactic restrictions on constexpr functions are
retained:
- asm-declarations are not permitted.
- try-blocks and function-try-blocks are not permitted.
- Declarations of variables with static and thread storage duration have some restrictions (see below).
我们可以在 N4140 部分 7.1.5
[dcl.constexpr] 中找到它,它说:
The definition of a constexpr function shall satisfy the following constraints:
it shall not be virtual (10.3);
its return type shall be a literal type;
each of its parameter types shall be a literal type;
its function-body shall be = delete, = default, or a compound-statement that does not contain
an asm-definition,
a goto statement,
a try-block, or
a definition of a variable of non-literal type or of static or thread storage duration or for which
no initialization is performed.
最后一个示例显示了如何在 constexpr 中使用 incr
:
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)
的规则是:
- modification of an object (5.17, 5.2.6, 5.3.2) unless it is applied to a non-volatile lvalue of literal type
that refers to a non-volatile object whose lifetime began within the evaluation of e;
7.1.5
[dcl.constexpr]中的措辞向我们展示了为什么incr
是一个有效的constexpr:
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting
constexpr constructor, if no argument values exist such that an invocation of the function or constructor
could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no
diagnostic required.
如T.C修改后的例子:
constexpr int& as_lvalue(int&& i){ return i; }
constexpr int x = incr(as_lvalue(1)) ;
表明,我们确实可以使用 incr
作为核心常量表达式的子表达式,因此它不是病式的。
我从 N4140 的 §5.19/2 中得到这个例子:
constexpr int incr(int &n) {
return ++n;
}
据我所知,这不是 constexpr
函数。但是这段代码是用 clang 和 g++ 编译的。参见 live example。我在这里错过了什么?
As far as I can tell, this is not a
constexpr
function.
为什么这么说? §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 函数。
根据 C++14 的 constexpr 规则,可以在 constexpr 上下文中使用 incr
,例如:
constexpr int incr(int& n) {
return ++n;
}
constexpr int foo() {
int n = 0;
incr(n);
return n;
}
除非函数体完全不可能是 constexpr
(例如,无条件调用非 constexpr 函数),否则编译器没有理由在定义点产生错误。
constexpr 函数甚至可以在主体中包含 paths/branches 而不是 constexpr。只要它们从未在 constexpr context 中使用,您就不会收到错误。 例如:
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, "!");
在 C++14 中,constexpr 函数的规则放宽了,论文 N3597: Relaxing constraints on constexpr functions。该论文探讨了基本原理和效果,包括以下内容(强调我的):
As in C++11, the constexpr keyword is used to mark functions which the implementation is required to evaluate during translation, if they are used from a context where a constant expression is required. Any valid C++ code is permitted in constexpr functions, including the creation and modification of local variables, and almost all statements, with the restriction that it must be possible for a constexpr function to be used from within a constant expression. A constant expression may still have side-effects which are local to the evaluation and its result.
和:
A handful of syntactic restrictions on constexpr functions are retained:
- asm-declarations are not permitted.
- try-blocks and function-try-blocks are not permitted.
- Declarations of variables with static and thread storage duration have some restrictions (see below).
我们可以在 N4140 部分 7.1.5
[dcl.constexpr] 中找到它,它说:
The definition of a constexpr function shall satisfy the following constraints:
it shall not be virtual (10.3);
its return type shall be a literal type;
each of its parameter types shall be a literal type;
its function-body shall be = delete, = default, or a compound-statement that does not contain
an asm-definition,
a goto statement,
a try-block, or
a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.
最后一个示例显示了如何在 constexpr 中使用 incr
:
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)
的规则是:
- modification of an object (5.17, 5.2.6, 5.3.2) unless it is applied to a non-volatile lvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;
7.1.5
[dcl.constexpr]中的措辞向我们展示了为什么incr
是一个有效的constexpr:
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no diagnostic required.
如T.C修改后的例子:
constexpr int& as_lvalue(int&& i){ return i; }
constexpr int x = incr(as_lvalue(1)) ;
表明,我们确实可以使用 incr
作为核心常量表达式的子表达式,因此它不是病式的。