【问题标题】:Why does double negation change the value of C++ concept?为什么双重否定会改变 C++ 概念的价值?
【发布时间】:2021-10-06 15:07:49
【问题描述】:

我的一个朋友给我看了一个带有概念的 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> );

它被所有编译器接受:https://gcc.godbolt.org/z/e67qKoqce

这里D的概念和C的概念是一样的,唯一的区别是双重否定运算符!!,乍一看不会改变概念值。对于结构体A 来说,C 的概念是真的,D 的概念是假的。

你能解释一下为什么会这样吗?

【问题讨论】:

  • 不是答案,因为我不确定这是否正确,但这可能与b 不存在这一事实有关吗?如果添加b = true,则表达式的行为与预期相同。

标签: c++ language-lawyer c++20 c++-concepts


【解决方案1】:

这里D的概念和C的概念是一样的

他们不是。在检查满意度并分解为原子约束时,约束(和概念 ID)被规范化。

[temp.names]

8 概念 ID 是一个简单模板 ID,其中模板名称为 一个概念名称。概念 ID 是 bool 类型的纯右值,而不是 命名模板专业化。如果 概念的规范化约束表达式 ([temp.constr.decl]) 是 由指定的模板参数满足([temp.constr.constr]) 否则为假。

||CD 中被视为不同:

[temp.constr.normal]

2 表达式E 的正常形式是定义的约束 如下:

  • 表达式( E ) 的正规形式是E 的正规形式。
  • 表达式E1 || E2 的范式是E1 和E2 范式的析取。
  • 表达式E1 &amp;&amp; E2的范式是E1E2范式的合取。
  • concept-id C&lt;A1, A2, ..., An&gt;的正规形式是C的约束表达式的正规形式,在替换A1之后, A2, ..., AnC 中的各个模板参数 每个原子约束中的参数映射。如果有任何此类替代 导致无效的类型或表达式,程序格式错误; 无需诊断。
  • 任何其他表达式E的正常形式是原子约束,其表达式为E,其参数映射为恒等式 映射。

对于C,原子约束是T::aT::b
对于D,只有一个原子约束!!(T::a || T::b)

原子约束中的替换失败使其不满足并评估为falseC&lt;A&gt; 是一个满足的约束和一个不满足的约束的析取,所以它是trueD&lt;A&gt; 为 false,因为它唯一的原子约束发生替换失败。

【讨论】:

    【解决方案2】:

    需要意识到的重要一点是,对于[temp.constr.constr],原子约束仅通过连词(通过顶级&amp;&amp;)和析取(通过顶级||)组成。否定必须被认为是约束的一部分,而不是约束的否定。甚至还有 non-normative note 明确指出这一点。

    考虑到这一点,我们可以检查这两种情况。 C 是两个原子约束的析取:T::aT::bPer /3,析取在检查满意度时采用短路行为。这意味着首先检查T::a。由于它成功了,整个约束C 得到满足,而无需检查第二个。

    另一方面,D 是一个原子约束:!!(T::a || T::b)|| 不会以任何方式创建析取,它只是表达式的一部分。我们查看[temp.constr.atomic]/3 来查看模板参数被替换了。这意味着T::aT::b 都执行了替换。该段还指出,如果替换失败,则不满足约束。正如前面的注释所暗示的,前面的否定甚至还没有被考虑。事实上,只有一个否定会产生相同的结果。


    现在显而易见的问题是为什么要以这种方式设计概念。不幸的是,我不记得在设计师的会议演讲和其他交流中遇到过任何理由。我能找到的最好的是原始提案中的这一点:

    虽然否定在我们的约束中相当普遍(参见第 5.3 节),但我们发现没有必要为运算符分配更深层次的语义。

    在我看来,这可能真的低估了做出决定的想法。我很想看到设计师详细说明这一点,因为我相信他比这个小引文还有更多要说的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-30
      • 1970-01-01
      • 1970-01-01
      • 2018-07-28
      • 1970-01-01
      • 2022-09-23
      相关资源
      最近更新 更多