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; }