mirror of
https://github.com/isocpp/CppCoreGuidelines.git
synced 2024-03-22 13:30:58 +08:00
template rule cleanup
This commit is contained in:
parent
c553535fb8
commit
6d1de28c91
|
@ -998,7 +998,7 @@ Insted, we could use `vector`:
|
|||
##### Note
|
||||
|
||||
The standards library and the GSL are examples of this philosophy.
|
||||
For example, instead of messing with the arrays, unions, cast, tricky lifetime issues, `GSL::owner`, etc.
|
||||
For example, instead of messing with the arrays, unions, cast, tricky lifetime issues, `gsl::owner`, etc.
|
||||
that is needed to implement key abstractions, such as `vector`, `span`, `lock_guard, and `future`, we use the libraries
|
||||
designed and implemented by people with more time and expertice than we usually have.
|
||||
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:
|
|||
* [T.22: Specify axioms for concepts](#Rt-axiom)
|
||||
* [T.23: Differentiate a refined concept from its more general case by adding new use patterns](#Rt-refine)
|
||||
* [T.24: Use tag classes or traits to differentiate concepts that differ only in semantics](#Rt-tag)
|
||||
* [T.25: Avoid negating constraints](#Rt-not)
|
||||
* [T.25: Avoid complementary constraints](#Rt-not)
|
||||
* [T.26: Prefer to define concepts in terms of use-patterns rather than simple syntax](#Rt-use)
|
||||
* ???
|
||||
|
||||
|
@ -12758,7 +12758,7 @@ Template definition rule summary:
|
|||
|
||||
* [T.60: Minimize a template's context dependencies](#Rt-depend)
|
||||
* [T.61: Do not over-parameterize members (SCARY)](#Rt-scary)
|
||||
* [T.62: Place non-dependent template members in a non-templated base class](#Rt-nondependent)
|
||||
* [T.62: Place non-dependent class template members in a non-templated base class](#Rt-nondependent)
|
||||
* [T.64: Use specialization to provide alternative implementations of class templates](#Rt-specialization)
|
||||
* [T.65: Use tag dispatch to provide alternative implementations of functions](#Rt-tag-dispatch)
|
||||
* [T.67: Use specialization to provide alternative implementations for irregular types](#Rt-specialization2)
|
||||
|
@ -13015,7 +13015,7 @@ Concept definition rule summary:
|
|||
* [T.22: Specify axioms for concepts](#Rt-axiom)
|
||||
* [T.23: Differentiate a refined concept from its more general case by adding new use patterns](#Rt-refine)
|
||||
* [T.24: Use tag classes or traits to differentiate concepts that differ only in semantics](#Rt-tag)
|
||||
* [T.25: Avoid negating constraints](#Rt-not)
|
||||
* [T.25: Avoid complimentary constraints](#Rt-not)
|
||||
* [T.26: Prefer to define concepts in terms of use-patterns rather than simple syntax](#Rt-use)
|
||||
* ???
|
||||
|
||||
|
@ -13457,7 +13457,7 @@ Prefer the standard-library ones.
|
|||
* The compiler flags ambiguous use of identical concepts.
|
||||
* Flag the definition of identical concepts.
|
||||
|
||||
### <a name="Rt-not"></a>T.25: Avoid negating constraints.
|
||||
### <a name="Rt-not"></a>T.25: Avoid complementary constraints
|
||||
|
||||
##### Reason
|
||||
|
||||
|
@ -13494,10 +13494,22 @@ version of `f()`, then delete it.
|
|||
|
||||
The compiler will select the overload and emit an appropriate error.
|
||||
|
||||
##### Note
|
||||
|
||||
Complementary constraints are unfortunately common in `enable_if` code:
|
||||
|
||||
template<typename T>
|
||||
enable_if<!C<T>,void> // bad
|
||||
f();
|
||||
|
||||
template<typename T>
|
||||
enable_if<C<T>,void>
|
||||
f();
|
||||
|
||||
|
||||
##### Enforcement
|
||||
|
||||
* Flag pairs of functions with `C<T>` and `!C<T>` constraints
|
||||
* Flag all constraint negation
|
||||
|
||||
### <a name="Rt-use"></a>T.26: Prefer to define concepts in terms of use-patterns rather than simple syntax
|
||||
|
||||
|
@ -13544,7 +13556,7 @@ In general, passing function objects gives better performance than passing point
|
|||
bool greater(double x, double y) { return x > y; }
|
||||
sort(v, greater); // pointer to function: potentially slow
|
||||
sort(v, [](double x, double y) { return x > y; }); // function object
|
||||
sort(v, greater<>); // function object
|
||||
sort(v, std::greater<>); // function object
|
||||
|
||||
bool greater_than_7(double x) { return x > 7; }
|
||||
auto x = find_if(v, greater_than_7); // pointer to function: inflexible
|
||||
|
@ -13812,8 +13824,8 @@ Because that's the best we can do without direct concept support.
|
|||
|
||||
##### Note
|
||||
|
||||
Beware of [negating constraints](# T.25).
|
||||
Faking concept overloading using `enable_if` sometimes forces us to use that error-prone designs.
|
||||
Beware of [complementary constraints](# T.25).
|
||||
Faking concept overloading using `enable_if` sometimes forces us to use that error-prone design technique.
|
||||
|
||||
##### Enforcement
|
||||
|
||||
|
@ -13861,11 +13873,24 @@ Eases tool creation.
|
|||
std::sort(begin(c),end(c)); // necessary and useful dependency
|
||||
}
|
||||
|
||||
??? // potentially surprising dependency
|
||||
template<typename Iter>
|
||||
Iter algo(Iter first, Iter last) {
|
||||
for (; first!=last; ++first) {
|
||||
auto x = sqrt(*first); // potentially surprising dependency: which sqrt()?
|
||||
helper(first,x); // potentially surprising dependency: heper is chosen based on first and x
|
||||
TT var = 7; // potentially surprising dependency: which TT?
|
||||
}
|
||||
}
|
||||
|
||||
##### Note
|
||||
|
||||
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.
|
||||
Templates typically appear in header files so their context dependencies are more vulnerable to `#include` order dependencies than functions in `.cpp` files.
|
||||
|
||||
##### Note
|
||||
|
||||
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 and invoke operations that does not exclusively operate on arguments.
|
||||
And don't get us started on macros!
|
||||
See also [T69](#???)
|
||||
|
||||
##### Enforcement
|
||||
|
@ -13938,11 +13963,11 @@ This looks innocent enough, but ???
|
|||
* Flag member types that do not depend on every template argument
|
||||
* Flag member functions that do not depend on every template argument
|
||||
|
||||
### <a name="Rt-nondependent"></a>T.62: Place non-dependent template members in a non-templated base class
|
||||
### <a name="Rt-nondependent"></a>T.62: Place non-dependent class template members in a non-templated base class
|
||||
|
||||
##### Reason
|
||||
|
||||
???
|
||||
Allow the base class members to be used without specifying template arguments and without template instantiation.
|
||||
|
||||
##### Example
|
||||
|
||||
|
@ -14003,15 +14028,63 @@ Specialization offers a powerful mechanism for providing alternative implementat
|
|||
|
||||
##### Reason
|
||||
|
||||
A template defines a general interface. ???
|
||||
A template defines a general interface.
|
||||
Tag dispatch allows us to select implmentations based on specific properties of an argument type.
|
||||
Performance.
|
||||
|
||||
##### Example
|
||||
|
||||
??? that's how we get algorithms like `std::copy` which compiles into a `memmove` call if appropriate for the arguments.
|
||||
This is a simplified version of `std::copy` (ignoring the possibility of non-contiguous sequences)
|
||||
|
||||
struct pod_tag {};
|
||||
struct non_pod_tag;
|
||||
|
||||
template<class T> struct copy_trait { using tag = non_pod_tag; }; // T is not "plain old data"
|
||||
|
||||
template<> struct copy_trait<int> { using tab = pod_tag; }; // int is "plain old data"
|
||||
|
||||
template<class Iter>
|
||||
Out copy_helper(Iter first, Iter last, Iter out, pog_tag)
|
||||
{
|
||||
// use memmove
|
||||
}
|
||||
|
||||
template<class Iter>
|
||||
Out copy_helper(Iter first, Iter last, Iter out, non_pod_tag)
|
||||
{
|
||||
// use loop calling copy constructors
|
||||
}
|
||||
|
||||
template<class Itert>
|
||||
Out copy(Iter first, Iter last, Iter out)
|
||||
{
|
||||
return copy_helper(first,last,out, typename copy_trait<Iter>::tag{})
|
||||
}
|
||||
|
||||
void use(vector<int>& vi, vector<int>& vi2, vector<string>& vs, vector<string>& vs2)
|
||||
{
|
||||
copy(vi.begin(),vi.end(), vi2.begin()); // uses memmove
|
||||
copy(vs.begin(),vs.end(), vs2.begin()); // uses a loop calling copy constructors
|
||||
}
|
||||
|
||||
This is a general and powerful technique for compile-time algorithm selection.
|
||||
|
||||
##### Note
|
||||
|
||||
When `concept`s become available such alternatives can be distinguished directly.
|
||||
When `concept`s become widely available such alternatives can be distinguished directly:
|
||||
|
||||
template<class Iter>
|
||||
requires Pod<Value_type_iter>
|
||||
Out copy_helper(In, first, In last, Out out)
|
||||
{
|
||||
// use memmove
|
||||
}
|
||||
|
||||
template<class Iter>
|
||||
Out copy_helper(In, first, In last, Out out)
|
||||
{
|
||||
// use loop calling copy constructors
|
||||
}
|
||||
|
||||
##### Enforcement
|
||||
|
||||
|
@ -14036,57 +14109,76 @@ When `concept`s become available such alternatives can be distinguished directly
|
|||
|
||||
##### Reason
|
||||
|
||||
???
|
||||
`()` is vulnerable to grammar ambiguities.
|
||||
|
||||
##### Example
|
||||
|
||||
???
|
||||
template<typename T, typename U>
|
||||
void f(T t, U u)
|
||||
{
|
||||
T v1(x); // is v1 a function of a variable?
|
||||
T v2 {x}; // variable
|
||||
auto x = T(u); // construction or cast?
|
||||
}
|
||||
|
||||
f(1,"asdf); // bad: cast from const char* to int
|
||||
|
||||
##### Enforcement
|
||||
|
||||
???
|
||||
* flag `()` initializers
|
||||
* flag function-style casts
|
||||
|
||||
|
||||
### <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
|
||||
|
||||
##### Reason
|
||||
|
||||
To provide only intended flexibility, and avoid accidental environmental changes.
|
||||
Provide only intended flexibility.
|
||||
Avoid vulnarability to accidental environmental changes.
|
||||
|
||||
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).
|
||||
##### Example
|
||||
|
||||
There are three major ways to let calling code customize a template.
|
||||
|
||||
* Call a member function. Callers can provide any type with such a named member function.
|
||||
|
||||
template<class T>
|
||||
void test(T t)
|
||||
// Call a member function
|
||||
void test1(T t)
|
||||
{
|
||||
t.f(); // require T to provide f()
|
||||
}
|
||||
|
||||
* 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.
|
||||
|
||||
template<class T>
|
||||
void test(T t)
|
||||
void test2(T t)
|
||||
// Call a nonmember function without qualification
|
||||
{
|
||||
// require f(/*T*/) be available in caller's scope or in T's namespace
|
||||
f(t);
|
||||
f(t); // require f(/*T*/) be available in caller's scope or in T's namespace
|
||||
}
|
||||
|
||||
* 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.
|
||||
|
||||
template<class T>
|
||||
void test(T t)
|
||||
void test3(T t)
|
||||
// Invoke a "trait"
|
||||
|
||||
{
|
||||
// require customizing test_traits<> to get non-default functions/types
|
||||
test_traits<T>::f(t);
|
||||
test_traits<T>::value_type x;
|
||||
test_traits<T>::f(t); // require customizing test_traits<> to get non-default functions/types
|
||||
}
|
||||
|
||||
A trait is usually a type alias to compute a type,
|
||||
a `constexpr` function to compute a value,
|
||||
or a traditional traits template to be specialized on the user's type.
|
||||
|
||||
##### Note
|
||||
|
||||
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);`.
|
||||
An unqualified call becomes a customization point where any function `helper` in the namespace of `t`'s type can be invoked;
|
||||
this can cause problems like [unintentionally invoking unconstrained function templates](#Rt-unconstrained-adl).
|
||||
|
||||
|
||||
##### Enforcement
|
||||
|
||||
* 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.
|
||||
|
||||
|
||||
## <a name="SS-temp-hier"></a>T.temp-hier: Template and hierarchy rules:
|
||||
|
||||
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.
|
|||
|
||||
Note that `maul()` violates the a `T*` points to an individual object [Rule](#???).
|
||||
|
||||
**Alternative**: Use a proper container:
|
||||
**Alternative**: Use a proper (templatized) container:
|
||||
|
||||
void maul2(Fruit* p)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue
Block a user