From 23b7ceab9c91acaadff41490625843e1e51a08b6 Mon Sep 17 00:00:00 2001 From: Bjarne Stroustrup Date: Mon, 15 Feb 2016 15:38:02 -0500 Subject: [PATCH] a bit of fillout in the template section --- CppCoreGuidelines.md | 150 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 139 insertions(+), 11 deletions(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 3869025..4731486 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -1,6 +1,6 @@ # C++ Core Guidelines -February 2, 2016 +February 15, 2016 Editors: @@ -11918,10 +11918,13 @@ In general, passing function objects gives better performance than passing point bool greater_than_7(double x) { return x>7; } auto x = find_if(v, greater_than_7); // pointer to function: inflexible auto y = find_if(v, [](double x) { return x>7; }); // function object: carries the needed data - auto y = find_if(v, Greater_than(7)); // function object: carries the needed data + auto z = find_if(v, Greater_than(7)); // function object: carries the needed data - ??? these lambdas are crying out for auto parameters -- any objection to making the change? +You can, of course, gneralize those functions using `auto` or (when and where available) concepts. For example: + auto y1 = find_if(v, [](Ordered x) { return x>7; }); // reruire an ordered type + auto z1 = find_if(v, [](auto x) { return x>7; }); // hope that the type has a > + ##### Note Lambdas generate function objects. @@ -11949,13 +11952,64 @@ The issue here is whether to require the minimal set of operations for a templat (e.g., `==` but not `!=` or `+` but not `+=`). The rule supports the view that a concept should reflect a (mathematically) coherent set of operations. +##### Example, bad + + class Minimal { + // ... + }; + + bool operator==(const Minimal&,const Minimal&); + bool operator<(const Minimal&,const Minimal&); + Minimal operator+(const Minimal&, const Minimal&); + // no other operators + + void f(const Minimal& x, const Minimal& y) + { + if (!(x==y) { /* ... */ } // OK + if (x!=y) { /* ... */ } //surprise! error + + while (!(x=y) { /* ... */ } //surprise! error + + x = x+y; // OK + x += y; // surprise! error + } + +This is minimal, but surprising and constraining for users. +It could even be less efficient. + ##### Example - ??? + class Convenient { + // ... + }; + + bool operator==(const Convenient&,const Convenient&); + bool operator<(const Convenient&,const Convenient&); + // ... and the other comparison operators ... + Minimal operator+(const Convenient&, const Convenient&); + // .. and the other arithmetic operators ... + + void f(const Convenient& x, const Convenient& y) + { + if (!(x==y) { /* ... */ } // OK + if (x!=y) { /* ... */ } //OK + + while (!(x=y) { /* ... */ } //OK + + x = x+y; // OK + x += y; // OK + } + +It can be a nuisance to define all operators, but not hard. +Hopefully, C++17 will give you comparison operators by default. + ##### Enforcement -??? +* Flag classes the support "odd" subsets of a set of operators, e.g., `==` but not `!=` or `+` but not `-`. +Yes, `std::string` is "odd", but it's too late to change that. ### T.42: Use template aliases to simplify notation and hide implementation details @@ -12042,29 +12096,74 @@ Flag uses where an explicitly specialized type exactly matches the types of the ##### Reason - ??? + Readability. + Preventing surprises and errors. + Most uses support that anyway. ##### Example - ??? + class X { + // ... + public: + explicit X(int); + X(const X&); // copy + X operator=(const X&); + X(X&&); // move + X& operator=(X&&); + ~X(); + // ... no moreconstructors ... + }; + + X x {1}; // fine + X y = x; // fine + std::vector v(10); // error: no default constructor + +##### Note + +Semiregular requires default constructible. ##### Enforcement -??? +* Flag types that are not at least `SemiRegular`. ### T.47: Avoid highly visible unconstrained templates with common names ##### Reason - ??? + An unconstrained template argument is a perfect match for anything so such a template can be preferred over more specific types that require minor conversions. + This is particularly annoying/dangerous when ADL is used. + Common names make this problem more likely. ##### Example - ??? + namespace Bad { + struct S { int m; }; + template + bool operator==(T1, T2) { cout << "Bad\n"; return true; } + } + + namespace T0 { + bool operator==(int, Bad::S) { cout << "T0\n"; return true; } // compate to int + + void test() + { + Bad::S bad{ 1 }; + vector v(10); + bool b = 1==bad; + bool b2 = v.size()==bad; + } + } + +This prints `T0` and `Bad`. + +Now the `==` in `Bad` was designed to cause trouble, but would you have spotted the problem in real code? +The problem is that `v.size()` returns an `unsigned` integer so that a conversion is needed to call the local `==`; +the `==` in `Bad` requires no conversions. +Realistic types, such as the standard library iterators can be made to exhibit similar anti-social tendencies. ##### Enforcement -??? +???? ### T.48: If your compiler does not support concepts, fake them with `enable_if` @@ -12295,6 +12394,35 @@ When `concept`s become available such alternatives can be distinguished directly ??? +### T.67: Use specialization to provide alternative implementations for irregular types + +##### Reason + + ??? + +##### Example + + ??? + +##### Enforcement + +??? + +### T.68: Use `{}` rather than `()` within templates to avoid ambiguities + +##### Reason + + ??? + +##### Example + + ??? + +##### Enforcement + +??? + + ### T.69: Inside a template, don't make an unqualified nonmember function call unless you intend it to be a customization point ##### Reason