为什么将“&& true”添加到约束中会使函数模板更好地重载?

Why does "&& true" added to a constraint make a function template a better overload?

考虑函数模板的以下两个重载 foo:

template <typename T>
void foo(T) requires std::integral<T> {
    std::cout << "foo requires integral\n";
}

template <typename T>
int foo(T) requires std::integral<T> && true {
    std::cout << "foo requires integral and true\n";
    return 0;
}

注意两个约束之间的区别:第二个有一个额外的 && true

直观地说,true 在连词中是多余的(因为 X && true 只是 X)。但是,看起来这会产生语义差异,因为 foo(42)call the second overload.

为什么会这样?具体来说,为什么第二个函数模板重载更好?

选择第二个特化是因为 C++ 使用偏序算法来选择泛型函数特化。

偏序通过变换选择两个函数模板中哪个比另一个更专业 每个模板依次(见下一段)并使用函数执行模板参数推导 类型。演绎过程确定模板之一是否比另一个更专业。 如果是这样,则更专门化的模板就是部分排序过程选择的模板。 如果两者都扣 成功,部分排序选择更受约束的模板(如果存在),如下所示。

13.5.4 约束部分排序

13.7.6.2 函数模板的部分排序

C++ 20 最终工作草案,可在此处找到: http://open-std.org/jtc1/sc22/wg21/docs/papers/2020/n4861.pdf

根据[temp.constr.order], particularly [temp.constr.order]/1 and [temp.constr.order]/3

/1 A constraint P subsumes a constraint Q if and only if, [...] [ Example: Let A and B be atomic constraints. The constraint A ∧ B subsumes A, but A does not subsume A ∧ B. The constraint A subsumes A ∨ B, but A ∨ B does not subsume A. Also note that every constraint subsumes itself. — end example ]

/3 A declaration D1 is at least as constrained as a declaration D2 if

  • (3.1) D1 and D2 are both constrained declarations and D1's associated constraints subsume those of D2; or
  • (3.2) D2 has no associated constraints.

如果我们将A视为std::integral<T>,将B视为true;然后:

  • A ∧ Bstd::integral<T> && true 包含 A,即 std::integral<T>,

意味着对于以下声明:

// Denote D1
template <typename T>
void foo(T) requires std::integral<T> && true;

// Denote D2
template <typename T>
void foo(T) requires std::integral<T>;

D1 的相关约束包含 D2 的约束,因此 D1 至少与 D2 一样受约束 ].同时,相反的情况不成立,D2 至少不像 D1 那样受到约束 。这意味着,根据 [temp.constr.order]/4

A declaration D1 is more constrained than another declaration D2 when D1 is at least as constrained as D2, and D2 is not at least as constrained as D1.

声明 D1 比声明 D2 更受约束,并且 D1 随后被重载决议选为最佳匹配, 根据 [temp.func.order]/2:

Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process. If both deductions succeed, the partial ordering selects the more constrained template (if one exists) as determined below.

约束 std::integral<T> && true 包含 std::integral<T>,因此根据约束规则的偏序“获胜”。

检查约束 A 是否包含 B ([temp.constr.order]):

1。两个约束都被引入 析取范式 。这意味着所有 || 都“扩展”为它们的独立形式。

2。然后每个 分离子句 被分成 atomic clauses (最小的 && 部分)。

3。不比较原子子句本身的含义,只比较它们是否相等。

如果约束 A 包含 B 的所有原子子句以及更多,则 A 包含 B。

参见Example 1

[Example 1: Let A and B be atomic constraints. The constraint A ∧ B subsumes A, but A does not subsume A ∧ B. The constraint A subsumes A ∨ B, but A ∨ B does not subsume A. Also note that every constraint subsumes itself. — end example]

因此,附加子句是空操作并不重要,它在 A 中但不在 B 中,实际上仅此而已。

Intuitively speaking, true is redundant in a conjunction

确实如此!为了采用标准语言,假设您的专业功能等效

Why is the second function template a better overload?

惊喜! 不是。

该标准在 格式错误的程序示例中明确引用了您的 && true 约束,不需要诊断

[13.5.2.3]

a program is ill-formed, no diagnostic required, when the meaning of the program depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent.

[Example 2:

template <unsigned N> void f2()
  requires Add1<2 * N>;
template <unsigned N> int f2()
  requires Add1<N * 2> && true;
void h2() {
  f2<0>();          // ill-formed, no diagnostic required:
                    // requires determination of subsumption between atomic constraints that are
                    // functionally equivalent but not equivalent
}

— end example]