约束包含仅适用于概念吗? [英] Does constraint subsumption only apply to concepts?

查看:82
本文介绍了约束包含仅适用于概念吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下示例:

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

正在调用 foo< int>()不明确,或约束 C1< T> && C2< T> 包含 C1< T>

Is the call foo<int>() ambiguous, or does the constraint C1<T> && C2<T> subsume C1<T>?

推荐答案

是。只能包含 概念。对 foo< int> 的调用是模棱两可的,因为两个声明都至少与另一个声明一样受约束。

Yes. Only concepts can be subsumed. The call to foo<int> is ambiguous because neither of the declarations is "at least as constrained as" the other.

但是,如果 C1 C2 都是 concept 而不是 inline constexpr bool s,然后声明 foo()返回 0 的约束至少应与 foo()的声明一样返回 1 ,对 foo< int> 的调用将有效并返回 0 。这是相对于任意布尔常量表达式更倾向于使用概念作为约束的原因之一。

If, however, C1 and C2 were both concepts instead of inline constexpr bools, then the declaration of the foo() that returns 0 would be at least as constrained as the declaration of the foo() that returns 1, and the call to foo<int> would be valid and return 0. This is one reason to prefer to use concepts as constraints over arbitrary boolean constant expressions.

最好以语义概念的约束匹配,值得一读(在这里我不会重述所有参数)。但以本文为例:

The reason for this difference (concepts subsume, arbitrary expressions do not) is best expressed in Semantic constraint matching for concepts, which is worth reading in full (I will not reproduce all the arguments here). But taking an example from the paper:


namespace X {
  template<C1 T> void foo(T);
  template<typename T> concept Fooable = requires (T t) { foo(t); };
}
namespace Y {
  template<C2 T> void foo(T);
  template<typename T> concept Fooable = requires (T t) { foo(t); };
}

X :: Fooable 等效于 Y :: Fooable ,尽管它们的含义完全不同(由于在不同的命名空间中定义)。这种偶然的等价性是有问题的:具有受这两个概念约束的函数的重载集将是模棱两可的。

X::Fooable is equivalent to Y::Fooable despite them meaning completely different things (by virtue of being defined in different namespace). This kind of incidental equivalence is problematic: an overload set with functions constrained by these two concepts would be ambiguous.

当一个概念偶然地完善了另一个概念时,这个问题就更加严重了。 / p>

That problem is exacerbated when one concept incidentally refines the others.

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

包含受约束的独特可行候选的重载集:: Fooable Y :: Fooable Z :: Fooable 选择受 Z :: Fooable 约束的候选人。

An overload set containing distinct viable candidates constrained by X::Fooable, Y::Fooable, and Z::Fooable respectively will always select the candidate constrained by Z::Fooable. This is almost certainly not what a programmer wants.






标准参考



包含规则位于 temp.constr.order] /1.2


一个原子约束 A 包含另一个原子约束 B 当且仅当 A B 使用[temp.constr.atomic]中所述的规则相同。

an atomic constraint A subsumes another atomic constraint B if and only if the A and B are identical using the rules described in [temp.constr.atomic].

原子约束在 [temp.constr.atomic]


原子约束由表达式 E 以及从 E 中出现的模板参数到涉及受约束对象的模板参数的模板参数的映射实体,称为参数映射([temp.constr.decl])。 [注意:原子约束是通过约束归一化形成的。 E 既不是逻辑 AND 表达式,也不是逻辑 OR 表达。 -[注释]

An atomic constraint is formed from an expression E and a mapping from the template parameters that appear within E to template arguments involving the template parameters of the constrained entity, called the parameter mapping ([temp.constr.decl]). [ Note: Atomic constraints are formed by constraint normalization. E is never a logical AND expression nor a logical OR expression. — end note ]

如果两个原子约束由相同的表达式和参数映射的目标根据[temp.over.link]中描述的表达式的规则是等效的。

Two atomic constraints are identical if they are formed from the same expression and the targets of the parameter mappings are equivalent according to the rules for expressions described in [temp.over.link].

这里的关键是原子约束是形成的。这是这里的重点。在 [temp.constr.normal]

The key here is that atomic constraints are formed. This is the key point right here. In [temp.constr.normal]:


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

The normal form of an expression E is a constraint that is defined as follows:

  • The normal form of an expression ( E ) is the normal form of E.
  • The normal form of an expression E1 || E2 is the disjunction of the normal forms of E1 and E2.
  • The normal form of an expression E1 && E2 is the conjunction of the normal forms of E1 and E2.
  • The normal form of an id-expression of the form C<A1, A2, ..., An>, where C names a concept, is the normal form of the constraint-expression of C, after substituting A1, A2, ..., An for C's respective template parameters in the parameter mappings in each atomic constraint. If any such substitution results in an invalid type or expression, the program is ill-formed; no diagnostic is required. [ ... ]
  • The normal form of any other expression E is the atomic constraint whose expression is E and whose parameter mapping is the identity mapping.

foo 的第一个重载,约束为 C1< T> && C2< T> ,因此要对其标准化,我们得到 C1< T> 1 C2< T> 1 ,然后就完成了。同样,对于 foo 的第二次重载,约束为 C1< T> 2 这是它自己的正常形式。

For the first overload of foo, the constraint is C1<T> && C2<T>, so to normalize it, we get the conjunction of the normal forms of C1<T>1 and C2<T>1 and then we're done. Likewise, for the second overload of foo, the constraint is C1<T>2 which is its own normal form.

使原子约束相同的规则是它们必须由相同的 expression (源代码级构造)形成。尽管这两个函数都具有使用令牌序列 C1< T> 的原子约束,但它们在源代码中不是相同的文字表达式

The rule for what makes atomic constraints identical is that they must be formed from the same expression (the source-level construct). While both functions hvae an atomic constraint which uses the token sequence C1<T>, those are not the same literal expression in the source code.

因此,下标表明它们实际上不是相同的原子约束。 C1< T> 1 C1< T> 2不同。规则不是令牌等效!因此,第一个 foo C1< T> 不包含第二个 foo C1< T> ,反之亦然。

Hence the subscripts indicating that these are, in fact, not the same atomic constraint. C1<T>1 is not identical to C1<T>2. The rule is not token equivalence! So the first foo's C1<T> does not subsume the second foo's C1<T>, and vice versa.

因此,模棱两可。

另一方面,如果我们有:

On the other hand, if we had:

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< T> && D2< T> 。第三个项目符号为我们提供了 D1< T> D2< T> 的结合。然后,第四个项目符号将我们替换为概念本身,因此第一个标准化为 true 1 ,第二个标准化为 true 2 。再次,下标指示所引用的 true

The constraint for the first function is D1<T> && D2<T>. The 3rd bullet gives us the conjunction of D1<T> and D2<T>. The 4th bullet then leads us to substitute into the concepts themselves, so the first one normalizes into true1 and the second into true2. Again, the subscripts indicate which true is being referred to.

第二个函数是 D1< T> ,该函数将第4个项目符号标准化为 true 1

The constraint for the second function is D1<T>, which normalizes (4th bullet) into true1.

现在, true 1 的确与<$相同。 c $ c> true 1 ,因此这些约束被认为是相同的。结果, D1< T> && D2< T> 包含 D1< T> quux< int>()是明确的调用,返回 0

And now, true1 is indeed the same expression as true1, so these constraints are considered identical. As a result, D1<T> && D2<T> subsumes D1<T>, and quux<int>() is an unambiguous call that returns 0.

这篇关于约束包含仅适用于概念吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆