J L*_*J L 16 c++ templates constraints c++-concepts c++20
这是 C++20 标准 (ISO/IEC 14882:2020) 第 13.5.4 节 ( [temp.constr.normal] ) 第 1 段中的示例(重点是我的):
\n\n\n概念 ID C<A1 , A2 , ..., An> 的范式是用A1 , A2 , ..., An 替换 C\xe2\x80\ 后 C 的约束表达式的范式每个原子约束中的参数映射中的 x99 各自的模板参数。如果任何此类替换导致无效类型或表达式,则该程序格式错误;无需诊断。
\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\n\nB\xe2\x80\x99s 约束表达式的规范化是有效的,并且会导致
\nT::value
(具有映射T -> U*
) Vtrue
(具有空映射),尽管该表达式T::value
对于指针类型来说格式不正确T
。C\xe2\x80\x99s 约束表达式的规范化会导致程序格式错误,因为它会V&*
在参数映射中形成无效类型。
我知道 C 会使程序格式错误(以及原因)。然而,我不清楚 B 是否会导致程序格式错误。文本指出 B\ 的规范化是有效的,但同时它指出T::value
由于该指针类型(我理解),该表达式的格式不正确。这是否意味着只有过程的规范化部分有效,但程序本身在后期检查时格式不正确T::value
?T::value
或者该程序在任何情况下都有效并且以某种方式跳过/避免了检查?
我检查了 Godbolt 的编译器资源管理器,GCC 和 Clang 似乎对此都很好。然而,由于标准说“不需要诊断”,这并没有多大帮助。
\nAmi*_*rsh 16
概念 B 是有效的,因为您可以将指针传递给概念 A。在 A 本身内部,指针无法访问::value
,但根据规范[temp.constr.atomic],这不会被视为错误,而是被视为false
,那么|| true
on 概念 A 将构成整个表达式true
。
请注意,如果我们将 int& 传递给概念 B,那么我们的代码将是 IFNDR,因为 B 会尝试将无效类型传递给 A ( int&*
)。
概念 C 本身就是 IFNDR,因为它传递了对 B 的引用,而 B 试图将指向该引用的指针传递给 A,并且类型再次V&*
无效。
尝试使用评估格式错误的无诊断表达式static_assert
不一定有助于回答表达式是否有效的问题,因为编译器不需要在格式错误的无诊断表达式static_assert
上失败。所需表达。
Tur*_*ght 13
请注意,每个规范化约束由两部分组成:
\n原子约束和关联的参数映射。
让我们将三个示例概念的每个约束分为这两部分:
\n在您的示例中,概念的规范化形式A
将是这两个约束的析取:
X::value
X \xe2\x86\xa6 T
true
概念的规范化形式B
是这两个约束的析取:
X::value
X \xe2\x86\xa6 U*
true
概念的规范化形式C
将是这两个约束的析取:
X::value
X \xe2\x86\xa6 V&*
true
形成原子表达式的参数映射很简单:
\n默认情况下,原子表达式总是以标识参数映射开始(即没有类型修改):
\n\n\n13.5.4 约束规范化 [[temp.constr.normal]]
\n
\n (1)表达式 E 的范式是一个约束,其定义如下:
\n[...]
\n (1.5)范式任何其他表达式 E 的原子约束,其表达式为 E ,其参数映射为恒等映射。
获得非恒等参数映射的唯一方法是在约束内命名另一个概念:
\n\n\n13.5.4 约束规范化 [[temp.constr.normal]]
\n
\n (1.4)概念 ID 的范式C<A1, A2, ..., An>
是 C 的约束表达式的范式,替换为 A1, A2, ..., An对于每个原子约束中的参数映射中的 C 各自的模板参数。[...]
这里有一些例子:
\ntemplate<class T> constexpr bool always_true = true;\n\n// Atomic constraint: always_true<X>\n// Parameter mapping: X \xe2\x86\xa6 T (identity)\ntemplate<class T> concept Base = always_true<T>;\n\n// Atomic constraint: always_true<X>\n// Parameter mapping: X \xe2\x86\xa6 U (identity) \ntemplate<class U> concept Foo = Base<U>;\n\n// Atomic constraint: always_true<X>\n// Parameter mapping: X \xe2\x86\xa6 V::type\ntemplate<class V> concept Bar = Base<typename V::type>;\n\n// Atomic constraint: always_true<X>\n// Parameter mapping: X \xe2\x86\xa6 W&&\ntemplate<class W> concept Baz = Base<W&&>;\n
Run Code Online (Sandbox Code Playgroud)\n这让我们回到了您最初引用的部分:
\n\n\n13.5.4 约束规范化 [[temp.constr.normal]]
\n
\n (1.4)概念 ID 的范式C<A1, A2, ..., An>
是 C 的约束表达式的范式,替换为 A1, A2, ..., An对于每个原子约束中的参数映射中的 C 各自的模板参数。如果任何此类替换导致无效类型或表达式,则该程序格式错误;无需诊断。
请注意,突出显示的语句仅适用于参数映射,不适用于原子表达式本身。
\n这就是为什么C
您的示例中的概念格式不正确,NDR - 因为其原子表达式的参数映射形成无效类型(指向引用的指针):X \xe2\x86\xa6 V&*
请注意,在规范化阶段替换的实际类型V
并不重要;唯一重要的是映射本身是否形成无效的类型或表达式。
这里还有几个例子:
\ntemplate<class T> constexpr bool always_true = true;\n\n// well-formed\n// Atomic constraint: always_true<X>\n// Parameter mapping: X \xe2\x86\xa6 T (identity)\ntemplate<class T> concept Base = always_true<T>;\n\n// well-formed\n// Atomic constraint: always_true<X>\n// Parameter mapping: X \xe2\x86\xa6 U::type\ntemplate<class U> concept Foo = Base<typename U::type>;\n\n// ill-formed, ndr (invalid parameter mapping)\n// Atomic constraint: always_true<X>\n// Parameter mapping: X \xe2\x86\xa6 V*::type\ntemplate<class V> concept Bar = Foo<V*>;\n\n// ill-formed, ndr (invalid parameter mapping)\n// Atomic constraint: always_true<X>\n// Parameter mapping: X \xe2\x86\xa6 W&*\ntemplate<class W> concept Baz = Foo<W&>;\n
Run Code Online (Sandbox Code Playgroud)\n为了回答程序何时出现格式错误的 ndr 问题,我们需要确定编译期间事件发生的顺序。
\n当确定关联声明的约束或评估概念的值时,将发生约束规范化。
\n这是由下式给出的:
\n\n13.5.4 约束规范化 [[temp.constr.normal]]
\n
\n [注 1]在确定声明的关联约束以及评估命名概念特化的 id 表达式的值时,执行约束表达式的规范化。
如果参数映射形成无效的类型或表达式,这就是您的程序将变得格式错误的地方。
\n约束标准化后,实际类型将被替换为约束:
\n\n\n13.5.2.3 原子约束 [[temp.constr.atomic]]
\n
\n (3)为了确定是否满足原子约束,首先将参数映射和模板实参代入其表达式中。如果替换导致无效类型或表达式,则不满足约束。
请注意,此时允许形成无效类型或表达式 - 如果是这种情况,则约束的结果将只是false
。
所以回答你的问题:
\n\n\n这是否意味着只有过程的规范化部分有效,但程序本身在后期检查时格式不正确
\nT::value
?
概念A
和B
结构良好。
\n概念格式C
不正确,ndr 在规范化过程中。
\n在这种情况下,实际的原子约束T::value
并不重要;也可以简单地是always_true<T>
。
\n\n或者程序在任何情况下都有效并且 T::value 的检查以某种方式被跳过/避免?
\n
只要概念C
从未标准化,该计划就是有效的。
\ni.e. 明确地评估它或使用它作为约束会使您的程序格式错误,ndr。
例子:
\n// evaluates concept C\n// -> results in normalization of C\n// -> ill-formed, ndr\nstatic_assert(C</* something */>); \n\ntemplate<C T>\nvoid foo() {}\n\n// constraints of foo will be determined\n// -> results in normalization of C\n// -> ill-formed, ndr\nfoo</* something */>(); \n
Run Code Online (Sandbox Code Playgroud)\n