约束包容是否仅适用于概念?

Does constraint subsumption only apply to concepts?

本文关键字:适用于 包容 是否 约束      更新时间:2023-10-16

考虑这个例子:

template <typename T> inline constexpr bool C1 = true;    
template <typename T> inline constexpr bool C2 = true;
template <typename T> requires C1<T> && C2<T> 
constexpr int foo() { return 0; }
template <typename T> requires C1<T> 
constexpr int foo() { return 1; }
constexpr int bar() {
return foo<int>();
}

调用foo<int>()模棱两可,还是约束C1<T> && C2<T>包含C1<T>

是的。只能包含概念。对foo<int>的呼吁是模棱两可的,因为两个声明都"至少像另一个声明一样受到约束"。

但是,如果C1C2都是concepts 而不是inline constexpr bools,那么返回0foo()的声明将至少与返回1foo()的声明一样受约束,并且对foo<int>的调用将是有效的,并且返回0。这是更喜欢使用概念作为约束而不是任意布尔常量表达式的原因之一。

<小时 />

背景

这种差异的原因(概念包含,任意表达式不包含)最好用概念的语义约束匹配来表达,值得完整阅读(我不会在这里复制所有参数)。但以论文为例:

namespace X {
template<C1 T> void foo(T);
template<typename T> concept Fooable = requires (T t) { foo(t); };
}
namespace Y {
template<C2 T> void foo(T);
template<typename T> concept Fooable = requires (T t) { foo(t); };
}

X::Fooable等同于Y::Fooable尽管它们意味着完全不同的东西(由于在不同的命名空间中定义)。这种偶然的等价是有问题的:受这两个概念约束的函数的重载集将是模棱两可的。

当一个概念偶然完善其他概念时,这个问题就会加剧。

namespace Z {
template<C3 T> void foo(T);
template<C3 T> void bar(T);
template<typename T> concept Fooable = requires (T t) {
foo(t);
bar(t);
};
}

包含分别受X::FooableY::FooableZ::Fooable约束的不同可行候选项的重载集将始终选择受Z::Fooable约束的候选项。这几乎肯定不是程序员想要的。

<小时 />

标准参考文献

包含规则位于 [temp.constr.order]/1.2 中:

一个原子约束 A包含另一个原子约束 B,当且仅当AB使用 [temp.constr.atomic] 中描述的规则相同时。

原子约束在 [temp.constr.atomic] 中定义:

原子约束由表达式E和从E中显示的模板参数到涉及受约束实体的模板参数的模板参数的映射(称为参数映射 ([temp.constr.decl])形成。[ 注意:原子约束由约束规范化形成。E从来都不是逻辑AND表达式,也不是逻辑OR表达式。— 尾注 ]

如果两个原子约束由同一表达式形成,则它们是相同的,并且根据 [temp.over.link 中所述的表达式规则,参数映射的目标等效。

这里的关键是形成了原子约束。这是这里的关键点。在 [temp.constr.normal] 中:

表达式E正常形式是一个约束,定义如下: