T.con-use: Concept use

T.10: Specify concepts for all template arguments

Reason

Correctness and readability. The assumed meaning (syntax and semantics) of a template argument is fundamental to the interface of a template. A concept dramatically improves documentation and error handling for the template. Specifying concepts for template arguments is a powerful design tool.

Example
template<typename Iter, typename Val>
    requires Input_iterator<Iter>
             && Equality_comparable<Value_type<Iter>, Val>
Iter find(Iter b, Iter e, Val v)
{
    // ...
}

or equivalently and more succinctly:

template<Input_iterator Iter, typename Val>
    requires Equality_comparable<Value_type<Iter>, Val>
Iter find(Iter b, Iter e, Val v)
{
    // ...
}
Note

Until your compilers support the concepts language feature, leave the concepts in comments:

template<typename Iter, typename Val>
    // requires Input_iterator<Iter>
    //       && Equality_comparable<Value_type<Iter>, Val>
Iter find(Iter b, Iter e, Val v)
{
    // ...
}
Note

Plain typename (or auto) is the least constraining concept. It should be used only rarely when nothing more than "it's a type" can be assumed. This is typically only needed when (as part of template metaprogramming code) we manipulate pure expression trees, postponing type checking.

References: TC++PL4, Palo Alto TR, Sutton

Enforcement

Flag template type arguments without concepts

T.11: Whenever possible use standard concepts

Reason

"Standard" concepts (as provided by the GSL, the ISO concepts TS, and hopefully soon the ISO standard itself) saves us the work of thinking up our own concepts, are better thought out than we can manage to do in a hurry, and improves interoperability.

Note

Unless you are creating a new generic library, most of the concepts you need will already be defined by the standard library.

Example
concept<typename T>
// don't define this: Sortable is in the GSL
Ordered_container = Sequence<T> && Random_access<Iterator<T>> && Ordered<Value_type<T>>;

void sort(Ordered_container& s);

This Ordered_container is quite plausible, but it is very similar to the Sortable concept in the GSL (and the Range TS). Is it better? Is it right? Does it accurately reflect the standard's requirements for sort? It is better and simpler just to use Sortable:

void sort(Sortable& s);   // better
Note

The set of "standard" concepts is evolving as we approach real (ISO) standardization.

Note

Designing a useful concept is challenging.

Enforcement

Hard.

  • Look for unconstrained arguments, templates that use "unusual"/non-standard concepts, templates that use "homebrew" concepts without axioms.
  • Develop a concept-discovery tool (e.g., see an early experiment).

T.12: Prefer concept names over auto for local variables

Reason

auto is the weakest concept. Concept names convey more meaning than just auto.

Example
vector<string> v;
auto& x = v.front();     // bad
String& s = v.begin();   // good
Enforcement
  • ???

T.13: Prefer the shorthand notation for simple, single-type argument concepts

Reason

Readability. Direct expression of an idea.

Example

To say "T is Sortable":

template<typename T>       // Correct but verbose: "The parameter is
    requires Sortable<T>   // of type T which is the name of a type
void sort(T&);             // that is Sortable"

template<Sortable T>       // Better: "The parameter is of type T
void sort(T&);             // which is Sortable"

void sort(Sortable&);      // Best: "The parameter is Sortable"

The shorter versions better match the way we speak. Note that many templates don't need to use the template keyword.

Enforcement
  • Not feasible in the short term when people convert from the <typename T> and <class T> notation.
  • Later, flag declarations that first introduces a typename and then constrains it with a simple, single-type-argument concept.