这是C20标准(ISO/IEC 14882:2020)第13.5.4节(temp.constr.normal(https://eel.is/cdraft/temp.constr.normal))第1段的示例(着重号为我):
概念id C〈A1,A2,...,An〉的范式是在用A1,A2,...,An替换每个原子约束中的参数Map中C的相应模板参数之后,C的 constraint-expression 的范式。如果任何这样的替换导致无效类型或表达式,则程序是病态的;不需要诊断。
template<typename T> concept A = T::value || true;
template<typename U> concept B = A<U*>;
template<typename V> concept C = B<V&>;
B的 constraint-expression 的规范化有效,结果为T::value
(使用MapT -> U*
)V true
(具有空Map),尽管表达式T::value
对于指针类型T
是病态形式的。C的 constraint-expression 的规范化导致程序是病态形式的,因为它将在参数Map中形成无效类型V&*
。
我知道C语言会使程序格式错误(以及为什么)。然而,我不清楚B是否会导致程序是病态的。文本声明B的规范化是有效的,但同时声明表达式T::value
由于该指针类型而为病态形式这是否意味着只有过程的规范化部分是有效的,而程序本身在稍后阶段检查T::value
时是病态的?或者程序在任何情况下都有效,并且以某种方式跳过/避免了对T::value
的检查?
我检查了Godbolt的Compiler Explorer,GCC和Clang看起来都没问题,然而,由于标准说“* 不需要诊断 *",这没有多大帮助。
2条答案
按热度按时间ej83mcc01#
概念B是有效的,因为你可以传递一个指针给概念A。在A本身内部,指针不能访问
::value
,但是根据规范[temp.constr.atomic],这不会被认为是一个错误,而是false
,那么概念A上的|| true
将使整个表达式true
。注意,如果我们将int&传递给概念B,那么我们的代码将是IFNDR,因为B将试图传递给A一个无效类型(
int&*
)。概念C是IFNDR,因为它传递了一个对B的引用,B试图将指向该引用的指针传递给A,并且
V&*
类型也是无效的。尝试使用
static_assert
计算 * 格式错误的不需要诊断的 * 表达式不一定有助于回答表达式是否有效的问题,例如the compiler is not required to fail astatic_assert
on an ill-formed-no-diagnostic-required expression。s5a0g9ez2#
注意,每个规范化约束由2部分组成:
原子约束和关联的参数Map。
让我们将三个示例概念的每个约束分为两部分:
在您的示例中,概念
A
的规范化形式将是这两个约束的析取:X::value
参数Map:
X ↦ T
true
无参数Map
概念
B
的规范化形式将是这两个约束的析取:X::value
参数Map:
X ↦ U*
true
无参数Map
概念
C
的规范化形式将是这两个约束的析取:X::value
参数Map:
X ↦ V&*
无参数Map
如何形成参数Map
形成原子表达式的参数Map非常简单:
默认情况下,原子表达式总是以标识参数Map开始(即没有类型修改):
13.5.4约束标准化临时约束标准
(1)表达式E的标准形式是如下定义的约束:
[...]
(1.5)任何其他表达式E的范式是原子约束,其表达式是E**,其参数Map是恒等Map。
而获得非恒等参数Map的唯一方法是在约束内命名另一个概念:
13.5.4约束标准化临时约束标准
(1.4)concept-id
C<A1, A2, ..., An>
的范式是在用A1,A2,...,An替换每个原子约束中的参数Map中C的相应模板参数之后C,的约束表达式的范式。[...]以下是一些例子:
您引用的部分
这就把我们带回到你最初引用的部分:
13.5.4约束标准化临时约束标准
(1.4)概念id
C<A1, A2, ..., An>
的范式是在用A1、A2、...、An替换每个原子约束中的参数Map中C的相应模板参数之后C的约束表达式的范式。如果任何这样的替换导致无效类型或表达式,则程序是病态的;不需要诊断。注意,突出显示的语句只适用于参数Map,而不适用于原子表达式本身。
这就是为什么示例中的概念
C
是病态的NDR--因为其原子表达式的参数Map形成了一个无效类型(指向引用的指针):X ↦ V&*
注意,在规范化阶段,替换
V
的实际类型并不重要;唯一重要的是Map本身是否形成了无效的类型或表达式。下面是几个例子:
编译期间事件的大致时间线
要回答程序何时出现格式错误的ndr的问题,我们需要确定编译过程中事件发生的顺序。
这由下式给出:
13.5.4约束标准化临时约束标准
[Note 1]当确定声明的关联约束时以及当评估命名概念专门化的id表达式的值时,执行约束表达式的规范化。
如果参数Map形成了无效的类型或表达式,那么这就是你的程序将变成格式错误的地方。
13.5.2.3 Atomic constraints temp.constr.atomic
(3)若要确定是否满足原子约束,首先将参数Map和模板参数替换到其表达式中。如果替换导致无效的类型或表达式,则不满足约束。
请注意,此时允许形成无效类型或表达式-如果是这种情况,则约束的结果将简单地为
false
。结论
为了回答你们的问题:
T::value
时是病态的?概念
A
和B
是合式的。概念
C
在规范化过程中是病态的。实际原子约束
T::value
在这种情况下并不重要;它也可以简单地为always_true<T>
。只要概念
C
从未被规范化,该程序就有效。也就是说,显式地计算它或将它用作约束将使程序格式不正确。
示例: