Bar*_*rry 17 c++ c++-concepts c++20
考虑这个例子:
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>();
}
Run Code Online (Sandbox Code Playgroud)
调用是foo<int>()
不明确的,还是约束C1<T> && C2<T>
包含C1<T>
?
Bar*_*rry 19
是.只有概念可以包含在内.调用foo<int>
是模棱两可的,因为这两个声明都没有"至少像另一个那样受限制".
但是,如果C1
和s C2
都是concept
s而不是inline constexpr bool
s,那么foo()
返回0
的声明至少会像foo()
返回的声明那样受到约束1
,并且调用foo<int>
将是有效的并返回0
.这是优先使用概念作为任意布尔常量表达式的约束的一个原因.
这种差异的原因(概念包含,任意表达式没有)最好用概念的语义约束匹配来表达,这值得完全阅读(我不会在这里重现所有的参数).但是从论文中举一个例子:
Run Code Online (Sandbox Code Playgroud)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
尽管它们意味着完全不同的东西(由于在不同的命名空间中定义).这种偶然的等价是有问题的:具有受这两个概念约束的函数的重载集将是模糊的.当一个概念偶然地改进其他概念时,这个问题就会恶化.
Run Code Online (Sandbox Code Playgroud)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的正常形式的结合.
- 一个的正常形态ID表达的C型<A 1,A 2,..., A N>,其中C名称的概念,是正常形式约束表达的C,取代甲后1, A 2,...,A n表示每个原子约束中参数映射中C的相应模板参数.如果任何此类替换导致无效的类型或表达,则该程序格式不正确; 无需诊断.[...]
- 任何其他表达式的正常形式是其表达式
E
为的原子约束,E
其参数映射是标识映射.
对于第一个重载foo
,约束是C1<T> && C2<T>
,所以为了规范化它,我们得到正常形式C1<T>
1和C2<T>
1的结合然后我们就完成了.同样,对于第二个重载foo
,约束是C1<T>
2,这是它自己的正常形式.
使原子约束相同的原则是它们必须由相同的表达式(源级构造)构成.虽然这两个函数都具有使用标记序列的原子约束C1<T>
,但它们与源代码中的文字表达式不同.
因此,下标表明这些实际上不是相同的原子约束.C1<T>
1与C1<T>
2不同.规则不是令牌等价!因此,第一个foo
的C1<T>
不归入第二foo
的C1<T>
,反之亦然.
因此,含糊不清.
另一方面,如果我们有:
template <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; }
Run Code Online (Sandbox Code Playgroud)
第一个函数的约束是D1<T> && D2<T>
.第三子弹给我们的结合D1<T>
和D2<T>
.第四个子弹然后引导我们替换概念本身,所以第一个归一化为true
1,第二个归一化为true
2.同样,下标表示正在引用哪个 true
.
第二个函数的约束是D1<T>
,将(第4个子弹)标准化为true
1.
而现在,true
1确实与true
1的表达式相同,因此这些约束被认为是相同的.因此,D1<T> && D2<T>
包含D1<T>
,并quux<int>()
返回一个明确的调用0
.
归档时间: |
|
查看次数: |
425 次 |
最近记录: |