Fed*_*dor 49 c++ language-lawyer c++-concepts c++20
我的一个朋友向我展示了一个带有概念的 C++20 程序,这让我感到困惑:
struct A { static constexpr bool a = true; };
template <typename T>
concept C = T::a || T::b;
template <typename T>
concept D = !!(T::a || T::b);
static_assert( C<A> );
static_assert( !D<A> );
Run Code Online (Sandbox Code Playgroud)
它被所有编译器接受:https : //gcc.godbolt.org/z/e67qKoqce
这里的概念与概念D相同C,唯一的区别是双重否定运算符!!,乍一看不会改变概念值。仍然对于 struct 来说,A这个概念C是正确的,而这个概念D是错误的。
你能解释一下为什么会这样吗?
Sto*_*ica 40
这里的概念与概念
D相同C
他们不是。当检查满意度并分解为原子约束时,约束(和概念 ID)被规范化。
[临时名称]
8概念 ID 是简单模板 ID,其中模板名称是概念名称。概念 ID 是 bool 类型的纯右值,并且不命名模板特化。如果概念的规范化约束表达式 ([temp.constr.decl]) 被指定的模板参数满足 ([temp.constr.constr]),则概念 ID 评估为真,否则为假。
并且||在C和 中被视为不同D:
[temp.constr.normal]
2表达式的标准形式
E是一个约束,其定义如下:
- 表达式
( E )的范式是 的范式E。- 表达式
E1 || E2的范式是 E1 和 E2 范式的分离。- 表达式
E1 && E2的范式是E1和的范式的合取E2。- 一个概念-ID的正常形态
C<A1, A2, ..., An>是约束表达式的普通形式C,替换后A1,A2,...,An用于C在每个原子约束参数映射的各个模板的参数。如果任何此类替换导致无效类型或表达式,则程序格式错误;不需要诊断。- 任何其他表达式的标准形式
E是原子约束,其表达式为E且其参数映射为恒等映射。
对于C原子约束是T::a和T::b。
因为D只有一个原子约束是!!(T::a || T::b)。
原子约束中的替换失败使其不满足并评估为false。C<A>是一个满足的约束和一个不满足的约束的分离,所以它是true. D<A>是假的,因为它的一个也是唯一的原子约束有一个替换失败。
chr*_*ris 26
要意识到的重要一点是,根据[temp.constr.constr],原子约束仅通过连接(通过 top-level &&)和分离(通过 top-level ||)组成。否定必须被视为约束的一部分,而不是约束的否定。甚至有一个非规范性说明明确指出了这一点。
考虑到这一点,我们可以检查这两种情况。C是两个原子约束的分离:T::a和T::b。根据 /3,在检查满意度时,析取采用短路行为。这意味着T::a首先检查。既然它成功了,整个约束C就得到了满足,而无需检查第二个。
D,在另一方面,是一个原子约束:!!(T::a || T::b)。The||不会以任何方式创建析取,它只是表达式的一部分。我们查看[temp.constr.atomic]/3以查看模板参数已被替换。这意味着T::a和T::b都已执行替换。本段还指出,如果替换失败,则不满足约束。正如前面的说明所暗示的那样,甚至还没有考虑前面的否定。事实上,只有一个否定会产生相同的结果。
现在显而易见的问题是为什么要以这种方式设计概念。不幸的是,我不记得在设计师的会议演讲和其他交流中遇到过任何理由。我能找到的最好的是原始提案中的这一点:
虽然否定在我们的约束中相当普遍(参见第 5.3 节),但我们发现没有必要为运算符分配更深层次的语义。
在我看来,这可能真的低估了做出决定的想法。我很想看到设计师详细说明这一点,因为我相信他有更多的话要说,而不是这个小小的引语。
| 归档时间: |
|
| 查看次数: |
2231 次 |
| 最近记录: |