在未经评估的上下文中使用概念会产生不一致的结果

康桓瑋*_*康桓瑋 9 c++ language-lawyer c++-concepts c++20

考虑以下无用的概念C

template<class T>
concept C = static_cast<T>(true);
Run Code Online (Sandbox Code Playgroud)

如果我们C在未评估的上下文中传递任意类型,那么所有三个编译器都将成功编译:

struct S {};
decltype(C<S>) x = 0;
Run Code Online (Sandbox Code Playgroud)

但是如果我们在未评估的上下文中传递intC

decltype(C<int>) y = 0;
Run Code Online (Sandbox Code Playgroud)

GCC 仍然接受它,而 Clang 和 MSVC 以相同的错误消息拒绝它

<source>:2:13: error: atomic constraint must be of type 'bool' (found 'int')
Run Code Online (Sandbox Code Playgroud)

上面的代码仍然格式良好吗?我应该信任哪个编译器?

Nic*_*las 13

概念名称并不像我们通常认为的那样在评估表达式的基础上起作用。一个概念名称解析为一个布尔值,它告诉约束表达式是否满足

概念 ID 是 bool 类型的纯右值,并且不命名模板特化。如果指定的模板参数满足概念的规范化约束表达式 ([temp.constr.constr]),则概念 ID 评估为真,否则为假

约束表达式被分解为原子片段。幸运的是,您的约束表达式只有一个原子部分:static_cast<T>(true). 我们解决原子约束是否满足的方法很简单。有几个部分。第一部分是:

为了确定是否满足原子约束,参数映射和模板参数首先被替换到其表达式中。如果替换导致无效类型或表达式,则不满足约束。

这就是编译器允许第一个的原因。static_cast<S>(true)不是有效的表达式,因为没有从 abool到 an 的转换S。因此,原子约束不满足,C<S>也是false

但是,static_cast<int>(true) 一个有效的表达式。所以我们继续第 2 部分:

否则,必要时执行左值到右值的转换,并且 E 应为 bool 类型的常量表达式。

这就是我们遇到“应该”这个词的地方。在标准语言中,“应”的意思是“如果用户提供的代码不是这种情况,则存在编译错误”。Anint不是“类型的常量表达式bool”。因此,代码不符合此要求。并导致编译错误。

我想 GCC 只是将错误视为替换失败(或者它会自动将其强制转换为 a bool),但标准要求 MSVC/Clang 的错误行为。