格式错误的约束表达式

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)

Art*_*yer 3

该代码正确的原因是[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/8xqre5bbThttps://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.