C++ 约束包容是否仅适用于概念?
考虑这个例子:C++ 约束包容是否仅适用于概念?,c++,c++-concepts,c++20,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; } templat
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>();
}
模板内联constexpr bool C1=true;
模板内联constexpr bool C2=true;
模板需要C1和C2
constexpr int foo(){return 0;}
模板需要C1
constexpr int foo(){return 1;}
constexpr int bar(){
返回foo();
}
调用foo()
是否不明确,或者约束C1和&C2是否包含C1
?是。只有概念可以包含在内。对foo
的调用是不明确的,因为两个声明都“至少没有”另一个声明那么受约束
但是,如果C1
和C2
都是概念而不是内联constexpr bool
s,那么返回0
的foo()
的声明将至少与返回1
的foo()
的声明一样受到约束,对foo
的调用将有效并返回0
。这是一个喜欢使用概念作为约束而不是任意布尔常量表达式的原因
背景
造成这种差异的原因(概念包含在内,任意表达式不包含在内)最好用表示,这是值得完整阅读的(我不会在这里重复所有的论点)。但以论文中的一个例子为例:
包含分别受X::Fooable
、Y::Fooable
和Z::Fooable
约束的不同可行候选对象的重载集将始终选择受Z::Fooable
约束的候选对象。这几乎肯定不是程序员想要的
标准参考文献
包含规则如下所示:
原子约束A包含另一个原子约束B,当且仅当A和B使用[temp.Const.atomic]中描述的规则相同时
原子约束定义在:
原子约束由表达式E
和E
中出现的模板参数到涉及受约束实体模板参数的模板参数的映射形成,称为参数映射([temp.constr.decl])。[ 注意:原子约束是通过约束规范化形成的。E
从来不是逻辑和表达式,也不是逻辑或表达式- 尾注 ]
如果两个原子约束由相同的表达式构成,则它们是相同的,并且根据[temp.over.link]中描述的表达式规则,参数映射的目标是等效的
这里的关键是形成原子约束。这是这里的关键点。在:
表达式E
的标准形式是一种约束,定义如下:
- 表达式(E)的范式是E的范式
- 表达式E1 | | E2的范式是E1和E2范式的析取
- 表达式E1&&E2的范式是E1和E2的范式的结合
- 形式C的id表达式的标准形式,其中C命名一个概念,是在每个原子约束的参数映射中将A1、A2、…、an替换为C的相应模板参数后,C的约束表达式的标准形式。如果任何此类替换导致无效类型或表达式,则程序将无法运行rmed;无需诊断。[…]
- 任何其他表达式
E
的标准形式是原子约束,其表达式为E
,其参数映射为身份映射
对于foo
的第一个重载,约束是C1和&C2
,因此为了规范化它,我们得到了C1
1和C2
1的范式的结合,然后我们完成了。同样,对于foo
的第二个重载,约束是C1
2,这是它自己的范式
使原子约束相同的规则是,它们必须由相同的表达式(源代码级构造)形成。虽然两个函数都是使用标记序列C1
的原子约束,但它们在源代码中不是相同的文字表达式
因此,表明它们实际上不是相同的原子约束的下标。C1
1与C1
2不相同。规则不是令牌等价!因此第一个foo
的C1
不包含第二个foo
的C1
,反之亦然
因此,这是不明确的
另一方面,如果我们有:
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; }
模板概念D1=true;
模板概念D2=真;
模板需要D1和D2
constexpr int-qux(){return 0;}
模板需要D1
constexpr int-qux(){return 1;}
第一个函数的约束条件是D1&&D2
。第三个项目符号给出了D1
和D2
的连词。第四个项目符号随后引导我们替换为概念本身,因此第一个项目符号规范化为true
1,第二个项目符号规范化为true
2。同样,下标指示了哪个项目正在引用true
第二个函数的约束是D1
,它将(第四个项目符号)规范化为true
1
现在,true
1确实是与true
1相同的表达式,因此这些约束被认为是相同的。因此,D1和&D2
包含D1
,而qux()
是一个明确的调用,返回0
,鉴于术语“相同的表达式”,我认为这个参数很弱没有规范性定义。我理解P717的意图,我们将语义挂在“相同表达式”的解释上,但我希望看到它变得更具体。“相同表达式”的其他用法
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);
};
}
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; }