303*_*303 8 c++ language-lawyer c++20
关于 function f,模板化变量的非类型模板参数的替换约束表达式是b有效的常量表达式吗?C++20 标准的哪些特定措辞允许或禁止这种情况?
struct s { static constexpr bool v = true; };
template<auto> inline constexpr bool b = true;
constexpr bool f(auto x) requires b<x.v> { return true; }
static_assert(f(s{})); // clang ok, gcc nope, msvc ok
Run Code Online (Sandbox Code Playgroud)
来自 GCC 的错误消息:
<source>:3:36: error: missing template arguments before '<' token
3 | constexpr bool f(auto x) requires b<x.v> { return true; }
| ^
<source>:3:36: error: expected initializer before '<' token
<source>:4:15: error: 'f' was not declared in this scope
4 | static_assert(f(s{}));
| ^
Run Code Online (Sandbox Code Playgroud)
该代码正确的原因是[expr.const]p5中的措辞不适用:
表达式E是核心常量表达式,除非E的计算遵循抽象机 ([intro.execution]) 的规则,将计算以下其中一项:
- [...]
并且上述要点均不适用。由于x.v不应用左值到右值转换x,并且S::v是一个常量表达式,您可以对其应用左值到右值转换,x.v(当x是类型S且不是引用时)是常量表达式。
看起来 gcc 正在尝试解析b<x.v>为(b < x.v) >(作为两个关系运算符),因此会出现错误。这显然是一个错误,因为它是一个模板化实体b<,所以无论什么都应该b是一个模板。
此 gcc 错误发生的更多示例:https://godbolt.org/z/fP74hW1qs https://godbolt.org/z/19K4nYTjT
如果您使用noexcept说明符而不是requires您会收到一条有趣的附加错误消息:
https://godbolt.org/z/8xqre5bbT(https://godbolt.org/z/cPYbb6vsq用于尾随返回类型)
<source>:3:38: error: use of parameter outside function body before '.' token
3 | constexpr bool f(auto x) noexcept(b<x.v>) { return true; }
| ^
Run Code Online (Sandbox Code Playgroud)
这让我想到在某些时候在函数体之外命名函数参数会导致 GCC 错误,但当它位于requires.