- A+

Consider this example:

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

Is the call `foo<int>()`

ambiguous, or does the constraint `C1<T> && C2<T>`

subsume `C1<T>`

?

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.

If, however, `C1`

and `C2`

were both `concept`

s instead of `inline constexpr bool`

s, 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.

### Background

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`

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

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.

### Standard References

The subsumption rule is in [temp.constr.order]/1.2:

an atomic constraint

Asubsumes another atomic constraintBif and only if theAandBare identical using the rules described in [temp.constr.atomic].

Atomic constraints are defined in [temp.constr.atomic]:

An

atomic constraintis 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 ]Two atomic constraints are

identicalif they are formed from the sameexpressionand the targets of the parameter mappings are equivalent according to the rules for expressions described in [temp.over.link].

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

The

normal formof anexpression`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-expressionof the form C<A_{1}, A_{2}, ..., A_{n}>, where C names a concept, is the normal form of theconstraint-expressionof C, after substituting A_{1}, A_{2}, ..., A_{n}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.

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.

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.

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.

Hence, ambiguous.

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

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 `true`

_{1} and the second into `true`

_{2}. Again, the subscripts indicate *which* `true`

is being referred to.

The constraint for the second function is `D1<T>`

, which normalizes (4th bullet) into `true`

_{1}.

And now, `true`

_{1} is indeed the same expression as `true`

_{1}, 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`

.