约束包容是否仅适用于概念?
Does constraint subsumption only apply to concepts?
考虑这个例子:
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>
的呼吁是模棱两可的,因为两个声明都"至少像另一个声明一样受到约束"。
但是,如果C1
和C2
都是concept
s 而不是inline constexpr bool
s,那么返回0
的foo()
的声明将至少与返回1
的foo()
的声明一样受约束,并且对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::Fooable
、Y::Fooable
和Z::Fooable
约束的不同可行候选项的重载集将始终选择受Z::Fooable
约束的候选项。这几乎肯定不是程序员想要的。
标准参考文献
包含规则位于 [temp.constr.order]/1.2 中:
一个原子约束 A包含另一个原子约束 B,当且仅当A和B使用 [temp.constr.atomic] 中描述的规则相同时。
原子约束在 [temp.constr.atomic] 中定义:
原子约束由表达式
E
和从E
中显示的模板参数到涉及受约束实体的模板参数的模板参数的映射(称为参数映射 ([temp.constr.decl])形成。[ 注意:原子约束由约束规范化形成。E
从来都不是逻辑AND
表达式,也不是逻辑OR
表达式。— 尾注 ]如果两个原子约束由同一表达式形成,则它们是相同的,并且根据 [temp.over.link 中所述的表达式规则,参数映射的目标等效。
这里的关键是形成了原子约束。这是这里的关键点。在 [temp.constr.normal] 中:
表达式
E
的正常形式是一个约束,定义如下:
- 表达式(E)的范式是E的范式。
- 表达式 E1 的范式 ||E2 是 E1 和 E2 正常形式的析取。
- 表达式 E1 && E2 的正常形式是 E1 和 E2 的正常形式的结合。
形式为 Ctemplate <typename T> concept D1 = true; template <typename T> concept D2 = true; template <typename T> requires D1<T> && D2<T> constexpr int quux() { return 0; } template <typename T> requires D1<T> constexpr int quux() { return 1; }
第一个函数的约束是
D1<T> && D2<T>
。第 3 个项目符号为我们提供了D1<T>
和D2<T>
的结合.然后,第 4 个项目符号引导我们代入概念本身,因此第一个项目符号规范化为true
1,第二个项目符号规范化为true
2。同样,下标指示所引用的true
。第二个函数的约束是
D1<T>
,它将 (第 4 个项目符号) 规范化为true
1。现在,
true
1确实与true
1的表达式相同,因此这些约束被认为是相同的。结果,D1<T> && D2<T>
包含D1<T>
,quux<int>()
是一个返回0
的明确调用。
- OpenGL - 在 NDC 中计算位置适用于着色器,但不适用于'regular'程序
- 使用模板参数重载C++方法:如何使其适用于模板的子类?
- 如何修复我的最大公约数代码?它适用于除零和零以外的所有数字
- 选择排序C++(已修改)并非适用于所有情况
- 无法让"std::enable_if"适用于无作用域枚举
- 请找出我的代码中的错误,它在提交得到错误答案的同时仍然适用于我的所有测试用例
- 确定夏令时是否适用于特定日期
- 是否有一种 STL 算法可以最后找到,但它也适用于指针?
- QT 样式表主题,适用于使用属性选择器的整个应用程序
- C++带有适用于左值和右值的引用参数的函数
- 代码适用于调试,但不适用于发布
- C++17 和更新的 std::分配器是否适用于动态数量的自定义堆?
- 适用于大型数组的无复制线程安全环形缓冲区
- NRVO 是否也适用于协程?
- 约束包容是否仅适用于概念?
- 程序只适用于包含(无副作用)cout声明
- 适用于频繁更改的大型数据集的最佳数据结构
- 相同的 for 循环适用于其他项目,但不适用于此项目。为什么?
- 无法让 FastNoise 适用于草地
- 其中列 = 值,仅适用于整数值