T.140: Name all operations with potential for reuse
Documentation, readability, opportunity for reuse.
structRec{
string name;
string addr;int id;// unique identifier};boolsame(const Rec& a,const Rec& b){return a.id == b.id;}
vector<Rec*>find_id(const string& name);// find all records for "name"auto x =find_if(vr.begin(), vr.end(),[&](Rec& r){if(r.name.size()!= n.size())returnfalse;// name to compare to is in nfor(int i =0; i < r.name.size();++i)if(tolower(r.name[i])!=tolower(n[i]))returnfalse;returntrue;});
There is a useful function lurking here (case insensitive string comparison), as there often is when lambda arguments get large.
boolcompare_insensitive(const string& a,const string& b){if(a.size()!= b.size())returnfalse;for(int i =0; i < a.size();++i)if(tolower(a[i])!=tolower(b[i]))returnfalse;returntrue;}auto x =find_if(vr.begin(), vr.end(),[&](Rec& r){compare_insensitive(r.name, n);});
Or maybe (if you prefer to avoid the implicit name binding to n):
auto cmp_to_n =[&n](const string& a){returncompare_insensitive(a, n);};auto x =find_if(vr.begin(), vr.end(),[](const Rec& r){returncmp_to_n(r.name);});
Exception: Lambdas logically used only locally, such as an argument to for_each and similar control flow algorithms.
Lambdas as initializers
T.141: Use an unnamed lambda if you need a simple function object in one place only
That makes the code concise and gives better locality than alternatives.
auto earlyUsersEnd = std::remove_if(users.begin(), users.end(),[](const User &a){return a.id >100;});
Exception: Naming a lambda can be useful for clarity even if it is used only once.
T.142: Use template variables to simplify notation
Generality. Reusability. Don't gratuitously commit to details; use the most general facilities available.
Example: Use != instead of < to compare iterators; != works for more objects because it doesn't rely on ordering.
for(auto i = first; i < last;++i){// less generic// ...}for(auto i = first; i != last;++i){// good; more generic// ...}// Of course, range-for is better still where it does what you want.
Example: Use the least-derived class that has the functionality you need.
classBase{public:
Bar f();
Bar g();};classDerived1:publicBase{public:
Bar h();};classDerived2:publicBase{public:
Bar j();};// bad, unless there is a specific reason for limiting to Derived1 objects onlyvoidmy_func(Derived1& param){use(param.f());use(param.g());}// good, uses only Base interface so only commit to thatvoidmy_func(Base& param){use(param.f());use(param.g());}
T.144: Don't specialize function templates
You can't partially specialize a function template per language rules.
You can fully specialize a function template but you almost certainly want to overload instead -- because function template specializations don't participate in overloading, they don't act as you probably wanted.
Rarely, you should actually specialize by delegating to a class template that you can specialize properly.
Exceptions: If you do have a valid reason to specialize a function template, just write a single function template that delegates to a class template, then specialize the class template (including the ability to write partial specializations).
T.150: Check that a class matches a concept using static_assert
If you intend for a class to match a concept, verifying that early saves users' pain.
Example
classX{public:X()=delete;X(const X&)=default;X(X&&)=default;
X&operator=(const X&)=default;// ...};
Somewhere, possibly in an implementation file, let the compiler check the desired properties of X:
static_assert(Default_constructible<X>);// error: X has no default constructorstatic_assert(Copyable<X>);// error: we forgot to define X's move constructor