当使用始终正确的概念来实现概念时,GCC 不同意 Clang 和 MSVC

wha*_*ned 16 c++ language-lawyer c++20

以下代码无法使用 Clang 13 和 MSVC v19.29 VS16.11 编译,但可以使用 GCC 11.2 成功编译。

template <typename...>
concept always_true = true;

template <typename T>
concept refable = always_true<T&>;

static_assert(refable<void>);
Run Code Online (Sandbox Code Playgroud)

铿锵13:

<source>:9:1: error: static_assert failed
static_assert(refable<void>);
^             ~~~~~~~~~~~~~
<source>:9:15: note: because 'void' does not satisfy 'refable'
static_assert(refable<void>);
              ^
<source>:7:32: note: because substituted constraint expression is ill-formed: cannot form a reference to 'void'
concept refable = always_true<T&>;
                               ^
Run Code Online (Sandbox Code Playgroud)

MSVC v19.29 VS16.11:

<source>(9): error C2607: static assertion failed
Run Code Online (Sandbox Code Playgroud)

神箭

GCC这里错了吗?我期望refable<void>评估结果为 false,因为它void&在直接上下文中形成无效的 type( ) 。

dfr*_*fri 15

\n
static_assert(refable<void>);\n
Run Code Online (Sandbox Code Playgroud)\n
\n

根据[temp.names]/8refable<void>是一个Concept-id

\n
\n

概念ID简单模板 ID,其中模板名称概念名称。Concept -id是 type 的纯右值bool,并且不命名模板特化。概念ID 的计算结果是指定的模板参数true 是否满足概念的规范化约束表达式false([temp.constr.constr]),否则。

\n
\n

因此,概念的规范化不依赖于给定概念 ID中命名概念的模板参数,而是独立执行的东西;然而,使用concept-id可能会触发执行概念规范化,[temp.constr.normal]/2

\n
\n

[注 1:在确定声明的关联约束 ([temp.constr.constr]) 以及评估命名概念专业化的 id 表达式的值时 ([expr.prim.id ])。\xe2\x80\x94 尾注]

\n
\n

约束标准化由[temp.constr.normal]/1涵盖,其中特别是 /1.4 控制 OP 的示例:

\n
\n

表达式 E 的范式是一个约束,定义如下:

\n
    \n
  • [...]

    \n
  • \n
  • /1.4概念 ID 的范式C<A1, A2, ..., An>的约束表达式的范式C,在替换A1, A2, ..., An每个原子约束 的参数映射中C各自的模板参数后。如果\n任何此类替换都会导致无效的类型或表达式,则该\n程序格式错误;无需诊断。

    \n

    [示例1:

    \n
    template<typename T> concept A = T::value || true;\ntemplate<typename U> concept B = A<U*>;\ntemplate<typename V> concept C = B<V&>;\n
    Run Code Online (Sandbox Code Playgroud)\n

    B\ 的约束表达式的规范化是有效的,并且会产生\n T\xe2\x80\x8b::\xe2\x80\x8bvalue(带有映射T\xe2\x86\xa6U*\xe2\x88\xa8 true(带有空\n映射),尽管该表达式T\xe2\x80\x8b::\xe2\x80\x8bvalue对于指针类型\n格式不正确T。约束表达式的规范化C会导致程序格式错误,因为它会V&*在参数映射中形成无效类型。\xe2\x80\x94结束示例]

    \n
  • \n
\n
\n

这里的关键问题是OP的例子是否遇到了

\n
\n

替换每个原子约束中的参数映射中的[...] 。如果任何此类替换导致无效类型或表达式,则该程序格式错误;无需诊断。

\n
\n

或不。

\n

refable然而,\ 的约束表达式的规范化会导致true(具有空映射),重点是此规范化约束中的空映射。因此,OP的refable<void> concept-id的规范形式将替换void为规范化形式的参数映射refable,但这仅包含一个空参数映射。因此,程序不是格式错误的,NDR 在 [temp.constr.normal]/1.4 下,并且参数映射永远不会失败,因为在单个空映射中没有任何内容可以替换。

\n

问题是否

\n
\n
refable<void>\n
Run Code Online (Sandbox Code Playgroud)\n
\n

满足 ( true) 约束或不满足 ( false) 约束的结果归结为如何解释[temp.constr.atomic]/3

\n
\n

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

\n
\n

这一点读起来有点棘手(特别是强调的部分),但由于参数映射是空的,因此参数映射不会出现替换失败,我将强调的部分解释为将模板参数替换为规范化约束-表达式,不包含模板参数,意味着不存在替换失败。这证明海湾合作委员会接受该计划实际上是正确的。

\n