根据C20标准,下面哪个例子是法律的C?
inline constexpr auto const& v = bool{}; // #1 - all ok
static_assert([]{
constexpr auto const& x = bool{}; // #2 - clang nope, gcc nope, msvc ok
return not x;
}());
struct s { constexpr s(auto const&) {} };
static_assert([]<s = bool{}>{ return true; }()); // #3 - clang nope, gcc ok, msvc ok
Live example
来自Clang的错误消息,示例为#2
:
<source>:5:27: error: constexpr variable 'x' must be initialized by a constant
expression
5 | constexpr auto const& x = bool{};
| ^ ~~~~~~
<source>:5:27: note: reference to temporary is not a constant expression
<source>:5:31: note: temporary created here
5 | constexpr auto const& x = bool{};
|
来自GCC的错误消息,示例为#2
:
<source>:5:36: error: '<anonymous>' is not a constant expression
5 | constexpr auto const& x = bool{};
| ^
<source>: At global scope:
<source>:7:2: error: non-constant condition for static assertion
4 | static_assert([]{
| ~~~
5 | constexpr auto const& x = bool{};
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6 | return not x;
| ~~~~~~~~~~~~~
7 | }());
| ~^~
<source>:7:2: error: '<lambda()>' called in a constant expression
<source>:4:15: note: '<lambda()>' is not usable as a 'constexpr' function because:
4 | static_assert([]{
| ^
来自Clang的错误消息,示例为#3
:
<source>:10:15: error: no matching function for call to object of type
'(lambda at <source>:10:15)'
10 | static_assert([]<s = bool{}>{ return true; }());
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:10:15: note: candidate template ignored: substitution failure: conversion
from 'bool' to 's' in converted constant expression would bind reference to a
temporary
10 | static_assert([]<s = bool{}>{ return true; }());
| ^ ~~~~
1条答案
按热度按时间mec1mxoz1#
这里的规则是
constexpr
引用 * 必须 * 引用一个具有静态存储持续时间的对象(这是现在[expr.const]/13中的“允许结果”规则)。也就是说,constexpr
引用必须有一个作为常量表达式的地址。这是很多人感到惊讶的事情,因为这是病态的:因为
p
需要指向具有恒定地址的对象,而每次调用f
时,a
都具有不同的地址。所以这行不通。这条规则解释了你的例子中发生了什么。这是因为
inline
:临时对象的生存期被延长为
v
的生存期,v
具有静态存储持续时间。这是一个固定地址。但这是不好的,因为同样的原因,我的简短的例子是不好的(MSVC在技术上是错误的接受这一点)-
x
在这里只是一个局部变量,它所引用的对象没有常量地址。[2]至少目前的规则是这样说的。这是一个相当令人困惑的规则,并阻止了许多看起来合理的代码工作。最突出的这种看起来合理的代码是结构化绑定:
这是因为
t2
的声明是对局部变量的constexpr引用。在P2686中,当前的方向是所谓的“符号寻址”。也就是说,我们并没有尝试用一个常量地址来初始化
p
(这不起作用),而是将其定义为“变量a
的地址“(依赖于它总是a
的地址,即使该地址不是常量)。您仍然不能将p
用作非类型模板参数(我认为大多数人对此不会感到惊讶),但它将允许大量合理的代码。报纸上有更多的细节。