Skip to content

Commit 6d1de28

Browse files
template rule cleanup
1 parent c553535 commit 6d1de28

File tree

1 file changed

+132
-40
lines changed

1 file changed

+132
-40
lines changed

CppCoreGuidelines.md

Lines changed: 132 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,7 @@ Insted, we could use `vector`:
998998
##### Note
999999

10001000
The standards library and the GSL are examples of this philosophy.
1001-
For example, instead of messing with the arrays, unions, cast, tricky lifetime issues, `GSL::owner`, etc.
1001+
For example, instead of messing with the arrays, unions, cast, tricky lifetime issues, `gsl::owner`, etc.
10021002
that is needed to implement key abstractions, such as `vector`, `span`, `lock_guard, and `future`, we use the libraries
10031003
designed and implemented by people with more time and expertice than we usually have.
10041004
Similarly, we can and should design and implement more specialized libraries, rather than leaving the users (often ourselves)
@@ -12738,7 +12738,7 @@ Concept definition rule summary:
1273812738
* [T.22: Specify axioms for concepts](#Rt-axiom)
1273912739
* [T.23: Differentiate a refined concept from its more general case by adding new use patterns](#Rt-refine)
1274012740
* [T.24: Use tag classes or traits to differentiate concepts that differ only in semantics](#Rt-tag)
12741-
* [T.25: Avoid negating constraints](#Rt-not)
12741+
* [T.25: Avoid complementary constraints](#Rt-not)
1274212742
* [T.26: Prefer to define concepts in terms of use-patterns rather than simple syntax](#Rt-use)
1274312743
* ???
1274412744

@@ -12758,7 +12758,7 @@ Template definition rule summary:
1275812758

1275912759
* [T.60: Minimize a template's context dependencies](#Rt-depend)
1276012760
* [T.61: Do not over-parameterize members (SCARY)](#Rt-scary)
12761-
* [T.62: Place non-dependent template members in a non-templated base class](#Rt-nondependent)
12761+
* [T.62: Place non-dependent class template members in a non-templated base class](#Rt-nondependent)
1276212762
* [T.64: Use specialization to provide alternative implementations of class templates](#Rt-specialization)
1276312763
* [T.65: Use tag dispatch to provide alternative implementations of functions](#Rt-tag-dispatch)
1276412764
* [T.67: Use specialization to provide alternative implementations for irregular types](#Rt-specialization2)
@@ -13015,7 +13015,7 @@ Concept definition rule summary:
1301513015
* [T.22: Specify axioms for concepts](#Rt-axiom)
1301613016
* [T.23: Differentiate a refined concept from its more general case by adding new use patterns](#Rt-refine)
1301713017
* [T.24: Use tag classes or traits to differentiate concepts that differ only in semantics](#Rt-tag)
13018-
* [T.25: Avoid negating constraints](#Rt-not)
13018+
* [T.25: Avoid complimentary constraints](#Rt-not)
1301913019
* [T.26: Prefer to define concepts in terms of use-patterns rather than simple syntax](#Rt-use)
1302013020
* ???
1302113021

@@ -13457,7 +13457,7 @@ Prefer the standard-library ones.
1345713457
* The compiler flags ambiguous use of identical concepts.
1345813458
* Flag the definition of identical concepts.
1345913459

13460-
### <a name="Rt-not"></a>T.25: Avoid negating constraints.
13460+
### <a name="Rt-not"></a>T.25: Avoid complementary constraints
1346113461

1346213462
##### Reason
1346313463

@@ -13494,10 +13494,22 @@ version of `f()`, then delete it.
1349413494

1349513495
The compiler will select the overload and emit an appropriate error.
1349613496

13497+
##### Note
13498+
13499+
Complementary constraints are unfortunately common in `enable_if` code:
13500+
13501+
template<typename T>
13502+
enable_if<!C<T>,void> // bad
13503+
f();
13504+
13505+
template<typename T>
13506+
enable_if<C<T>,void>
13507+
f();
13508+
13509+
1349713510
##### Enforcement
1349813511

1349913512
* Flag pairs of functions with `C<T>` and `!C<T>` constraints
13500-
* Flag all constraint negation
1350113513

1350213514
### <a name="Rt-use"></a>T.26: Prefer to define concepts in terms of use-patterns rather than simple syntax
1350313515

@@ -13542,14 +13554,14 @@ In general, passing function objects gives better performance than passing point
1354213554
##### Example (using TS concepts)
1354313555

1354413556
bool greater(double x, double y) { return x > y; }
13545-
sort(v, greater); // pointer to function: potentially slow
13557+
sort(v, greater); // pointer to function: potentially slow
1354613558
sort(v, [](double x, double y) { return x > y; }); // function object
13547-
sort(v, greater<>); // function object
13559+
sort(v, std::greater<>); // function object
1354813560

1354913561
bool greater_than_7(double x) { return x > 7; }
13550-
auto x = find_if(v, greater_than_7); // pointer to function: inflexible
13562+
auto x = find_if(v, greater_than_7); // pointer to function: inflexible
1355113563
auto y = find_if(v, [](double x) { return x > 7; }); // function object: carries the needed data
13552-
auto z = find_if(v, Greater_than<double>(7)); // function object: carries the needed data
13564+
auto z = find_if(v, Greater_than<double>(7)); // function object: carries the needed data
1355313565

1355413566
You can, of course, generalize those functions using `auto` or (when and where available) concepts. For example:
1355513567

@@ -13812,8 +13824,8 @@ Because that's the best we can do without direct concept support.
1381213824

1381313825
##### Note
1381413826

13815-
Beware of [negating constraints](# T.25).
13816-
Faking concept overloading using `enable_if` sometimes forces us to use that error-prone designs.
13827+
Beware of [complementary constraints](# T.25).
13828+
Faking concept overloading using `enable_if` sometimes forces us to use that error-prone design technique.
1381713829

1381813830
##### Enforcement
1381913831

@@ -13861,11 +13873,24 @@ Eases tool creation.
1386113873
std::sort(begin(c),end(c)); // necessary and useful dependency
1386213874
}
1386313875

13864-
??? // potentially surprising dependency
13876+
template<typename Iter>
13877+
Iter algo(Iter first, Iter last) {
13878+
for (; first!=last; ++first) {
13879+
auto x = sqrt(*first); // potentially surprising dependency: which sqrt()?
13880+
helper(first,x); // potentially surprising dependency: heper is chosen based on first and x
13881+
TT var = 7; // potentially surprising dependency: which TT?
13882+
}
13883+
}
1386513884

1386613885
##### Note
1386713886

13868-
Having a template operate only on its arguments would be one way of reducing the number of dependencies to a minimum, but that would generally be unmanageable. For example, an algorithm usually uses other algorithms.
13887+
Templates typically appear in header files so their context dependencies are more vulnerable to `#include` order dependencies than functions in `.cpp` files.
13888+
13889+
##### Note
13890+
13891+
Having a template operate only on its arguments would be one way of reducing the number of dependencies to a minimum, but that would generally be unmanageable.
13892+
For example, an algorithm usually uses other algorithms and invoke operations that does not exclusively operate on arguments.
13893+
And don't get us started on macros!
1386913894
See also [T69](#???)
1387013895

1387113896
##### Enforcement
@@ -13938,11 +13963,11 @@ This looks innocent enough, but ???
1393813963
* Flag member types that do not depend on every template argument
1393913964
* Flag member functions that do not depend on every template argument
1394013965

13941-
### <a name="Rt-nondependent"></a>T.62: Place non-dependent template members in a non-templated base class
13966+
### <a name="Rt-nondependent"></a>T.62: Place non-dependent class template members in a non-templated base class
1394213967

1394313968
##### Reason
1394413969

13945-
???
13970+
Allow the base class members to be used without specifying template arguments and without template instantiation.
1394613971

1394713972
##### Example
1394813973

@@ -14003,15 +14028,63 @@ Specialization offers a powerful mechanism for providing alternative implementat
1400314028

1400414029
##### Reason
1400514030

14006-
A template defines a general interface. ???
14031+
A template defines a general interface.
14032+
Tag dispatch allows us to select implmentations based on specific properties of an argument type.
14033+
Performance.
1400714034

1400814035
##### Example
1400914036

14010-
??? that's how we get algorithms like `std::copy` which compiles into a `memmove` call if appropriate for the arguments.
14037+
This is a simplified version of `std::copy` (ignoring the possibility of non-contiguous sequences)
14038+
14039+
struct pod_tag {};
14040+
struct non_pod_tag;
14041+
14042+
template<class T> struct copy_trait { using tag = non_pod_tag; }; // T is not "plain old data"
14043+
14044+
template<> struct copy_trait<int> { using tab = pod_tag; }; // int is "plain old data"
14045+
14046+
template<class Iter>
14047+
Out copy_helper(Iter first, Iter last, Iter out, pog_tag)
14048+
{
14049+
// use memmove
14050+
}
14051+
14052+
template<class Iter>
14053+
Out copy_helper(Iter first, Iter last, Iter out, non_pod_tag)
14054+
{
14055+
// use loop calling copy constructors
14056+
}
14057+
14058+
template<class Itert>
14059+
Out copy(Iter first, Iter last, Iter out)
14060+
{
14061+
return copy_helper(first,last,out, typename copy_trait<Iter>::tag{})
14062+
}
14063+
14064+
void use(vector<int>& vi, vector<int>& vi2, vector<string>& vs, vector<string>& vs2)
14065+
{
14066+
copy(vi.begin(),vi.end(), vi2.begin()); // uses memmove
14067+
copy(vs.begin(),vs.end(), vs2.begin()); // uses a loop calling copy constructors
14068+
}
14069+
14070+
This is a general and powerful technique for compile-time algorithm selection.
1401114071

1401214072
##### Note
1401314073

14014-
When `concept`s become available such alternatives can be distinguished directly.
14074+
When `concept`s become widely available such alternatives can be distinguished directly:
14075+
14076+
template<class Iter>
14077+
requires Pod<Value_type_iter>
14078+
Out copy_helper(In, first, In last, Out out)
14079+
{
14080+
// use memmove
14081+
}
14082+
14083+
template<class Iter>
14084+
Out copy_helper(In, first, In last, Out out)
14085+
{
14086+
// use loop calling copy constructors
14087+
}
1401514088

1401614089
##### Enforcement
1401714090

@@ -14036,57 +14109,76 @@ When `concept`s become available such alternatives can be distinguished directly
1403614109

1403714110
##### Reason
1403814111

14039-
???
14112+
`()` is vulnerable to grammar ambiguities.
1404014113

1404114114
##### Example
1404214115

14043-
???
14116+
template<typename T, typename U>
14117+
void f(T t, U u)
14118+
{
14119+
T v1(x); // is v1 a function of a variable?
14120+
T v2 {x}; // variable
14121+
auto x = T(u); // construction or cast?
14122+
}
14123+
14124+
f(1,"asdf); // bad: cast from const char* to int
1404414125

1404514126
##### Enforcement
1404614127

14047-
???
14128+
* flag `()` initializers
14129+
* flag function-style casts
14130+
1404814131

1404914132
### <a name="Rt-customization"></a>T.69: Inside a template, don't make an unqualified nonmember function call unless you intend it to be a customization point
1405014133

1405114134
##### Reason
1405214135

14053-
To provide only intended flexibility, and avoid accidental environmental changes.
14136+
Provide only intended flexibility.
14137+
Avoid vulnarability to accidental environmental changes.
1405414138

14055-
If you intend to call your own helper function `helper(t)` with a value `t` that depends on a template type parameter, put it in a `::detail` namespace and qualify the call as `detail::helper(t);`. Otherwise the call becomes a customization point where any function `helper` in the namespace of `t`'s type can be invoked instead -- falling into the second option below, and resulting in problems like [unintentionally invoking unconstrained function templates of that name that happen to be in the same namespace as `t`'s type](#Rt-unconstrained-adl).
14139+
##### Example
1405614140

1405714141
There are three major ways to let calling code customize a template.
1405814142

14059-
* Call a member function. Callers can provide any type with such a named member function.
14060-
1406114143
template<class T>
14062-
void test(T t)
14144+
// Call a member function
14145+
void test1(T t)
1406314146
{
1406414147
t.f(); // require T to provide f()
1406514148
}
1406614149

14067-
* Call a nonmember function without qualification. Callers can provide any type for which there is such a function available in the caller's context or in the namespace of the type.
14068-
1406914150
template<class T>
14070-
void test(T t)
14151+
void test2(T t)
14152+
// Call a nonmember function without qualification
1407114153
{
14072-
// require f(/*T*/) be available in caller's scope or in T's namespace
14073-
f(t);
14154+
f(t); // require f(/*T*/) be available in caller's scope or in T's namespace
1407414155
}
1407514156

14076-
* Invoke a "trait" -- usually a type alias to compute a type, or a `constexpr` function to compute a value, or in rarer cases a traditional traits template to be specialized on the user's type.
14077-
1407814157
template<class T>
14079-
void test(T t)
14080-
{
14081-
// require customizing test_traits<> to get non-default functions/types
14082-
test_traits<T>::f(t);
14083-
test_traits<T>::value_type x;
14158+
void test3(T t)
14159+
// Invoke a "trait"
14160+
14161+
{
14162+
test_traits<T>::f(t); // require customizing test_traits<> to get non-default functions/types
1408414163
}
1408514164

14165+
A trait is usually a type alias to compute a type,
14166+
a `constexpr` function to compute a value,
14167+
or a traditional traits template to be specialized on the user's type.
14168+
14169+
##### Note
14170+
14171+
If you intend to call your own helper function `helper(t)` with a value `t` that depends on a template type parameter,
14172+
put it in a `::detail` namespace and qualify the call as `detail::helper(t);`.
14173+
An unqualified call becomes a customization point where any function `helper` in the namespace of `t`'s type can be invoked;
14174+
this can cause problems like [unintentionally invoking unconstrained function templates](#Rt-unconstrained-adl).
14175+
14176+
1408614177
##### Enforcement
1408714178

1408814179
* In a template, flag an unqualified call to a nonmember function that passes a variable of dependent type when there is a nonmember function of the same name in the template's namespace.
1408914180

14181+
1409014182
## <a name="SS-temp-hier"></a>T.temp-hier: Template and hierarchy rules:
1409114183

1409214184
Templates are the backbone of C++'s support for generic programming and class hierarchies the backbone of its support
@@ -14162,7 +14254,7 @@ Never write such code.
1416214254

1416314255
Note that `maul()` violates the a `T*` points to an individual object [Rule](#???).
1416414256

14165-
**Alternative**: Use a proper container:
14257+
**Alternative**: Use a proper (templatized) container:
1416614258

1416714259
void maul2(Fruit* p)
1416814260
{

0 commit comments

Comments
 (0)