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.