* [T.144: Don't specialize function templates](#Rt-specialize-function)
* [T.150: Check that a class matches a concept using static_assert](#Rt-check-class)
* [T.150: Check that a class matches a concept using `static_assert`](#Rt-check-class)
* [T.??: ????](#Rt-???)
## <aname="SS-GP"></a>T.gp: Generic programming
@ -13179,7 +13180,8 @@ Static helps dynamic: Use static polymorphism to implement dynamically polymorph
##### Example
Dynamic helps static: Offer a generic, comfortable, statically bound interface, but internally dispatch dynamically, so you offer a uniform object layout. Examples include type erasure as with `std::shared_ptr`'s deleter. (But [don't overuse type erasure](#Rt-erasure).)
Dynamic helps static: Offer a generic, comfortable, statically bound interface, but internally dispatch dynamically, so you offer a uniform object layout.
Examples include type erasure as with `std::shared_ptr`'s deleter (but [don't overuse type erasure](#Rt-erasure)).
##### Note
@ -13187,9 +13189,15 @@ In a class template, nonvirtual functions are only instantiated if they're used
This can bloat code size, and may overconstrain a generic type by instantiating functionality that is never needed.
Avoid this, even though the standard-library facets made this mistake.
##### See also
* ref ???
* ref ???
* ref ???
##### Enforcement
* Flag a class template that declares new (non-inherited) virtual functions.
The compiler can determine refinement based on the sets of required operations.
The compiler can determine refinement based on the sets of required operations (here, suffix `++`).
This decreases the burden on implementers of these types since
they do not need any special declarations to "hook into the concept".
If two concepts have exactly the same requirements, they are logically equivalent (there is no refinement).
##### Enforcement
* Flag a concept that has exactly the same requirements as another already-seen concept (neither is more refined). To disambiguate them, see [T.24](#Rt-tag).
* Flag a concept that has exactly the same requirements as another already-seen concept (neither is more refined).
To disambiguate them, see [T.24](#Rt-tag).
### <aname="Rt-tag"></a>T.24: Use tag classes or traits to differentiate concepts that differ only in semantics.
@ -13707,6 +13716,18 @@ Complementary constraints are unfortunately common in `enable_if` code:
f();
##### Note
Complementary requirements on one requirements is sometimes (wrongly) considered manageable.
However, for two or more requirements the number of definitions needs can go up exponentially (2,4,9,16,...):
C1<T>&& C2<T>
!C1<T>&& C2<T>
C1<T>&& !C2<T>
!C1<T>&& !C2<T>
Now the opportunities for errors multiply.
##### Enforcement
* Flag pairs of functions with `C<T>` and `!C<T>` constraints
@ -13725,14 +13746,18 @@ You might be tempted to define a concept `Equality` like this:
Obviously, it would be better and easier just to use the standard `EqualityComparable`,
but - just as an example - if you had to define such a concept, prefer:
but - just as an example - if you had to define such a concept, prefer:
template<typenameT> concept Equality = requires(T a, T b) {
bool == { a==b }
bool == { a!=b }
// axiom { !(a==b)==(a!=b) }
// axiom { a=b; => a==b } // => means "implies"
}
as oposed to defining two meaningless concepts `has_equal` and `has_not_equal` just as helpers in the definition of `Equality`.
By "meaningless" we mean that we cannot specify the semantics of `has_equal` in isolation.
##### Enforcement
???
@ -13794,9 +13819,9 @@ Consider, a `sort` instrumented with (oversimplified) simple debug support:
void sort(Sortable& s) // sort sequence s
{
if (debug) cerr << "enter sort()\n";
if (debug) cerr << "enter sort( " <<s<<")\n";
// ...
if (debug) cerr << "exit sort()\n";
if (debug) cerr << "exit sort( " <<s<<")\n";
}
Should this be rewritten to:
@ -13805,9 +13830,9 @@ Should this be rewritten to:
requires Streamable<S>
void sort(S& s) // sort sequence s
{
if (debug) cerr << "enter sort()\n";
if (debug) cerr << "enter sort( " <<s<<")\n";
// ...
if (debug) cerr << "exit sort()\n";
if (debug) cerr << "exit sort( " <<s<<")\n";
}
After all, there is nothing in `Sortable` that requires `iostream` support.
@ -13826,6 +13851,8 @@ we may get a late compile-time error.
By not using concept checking for properties of a template argument that is not considered essential,
we delay checking until instantiation time.
We consider this a worthwhile tradeoff.
Note that using non-local, non-dependent names (such as `debug` and `cerr`) also introduce context dependencies that may lead to "mysterious" errors.
##### Note
@ -13839,7 +13866,10 @@ It can be hard to decide which properties of a type is essential and which are n
##### Reason
Improved readability. Implementation hiding. Note that template aliases replace many uses of traits to compute a type. They can also be used to wrap a trait.
Improved readability.
Implementation hiding.
Note that template aliases replace many uses of traits to compute a type.
They can also be used to wrap a trait.
##### Example
@ -13854,11 +13884,32 @@ This saves the user of `Matrix` from having to know that its elements are stored