@@ -465,7 +465,7 @@ portability will be impacted.
465
465
Using valid ISO C++ does not guarantee portability (let alone correctness).
466
466
Avoid dependence on undefined behavior (e.g., [undefined order of evaluation](#Res-order))
467
467
and be aware of constructs with implementation defined meaning (e.g., `sizeof(int)`).
468
-
468
+
469
469
##### Note
470
470
471
471
There are environments where restrictions on use of standard C++ language or library features are necessary, e.g., to avoid dynamic memory allocation as required by aircraft control software standards.
@@ -585,7 +585,7 @@ You don't need to write error handlers for errors caught at compile time.
585
585
cerr << "Int too small\n"
586
586
587
587
This example is easily simplified
588
-
588
+
589
589
// Int is an alias used for integers
590
590
static_assert(sizeof(Int) >= 4); // do: compile-time check
591
591
@@ -594,12 +594,12 @@ This example is easily simplified
594
594
void read(int* p, int n); // read max n integers into *p
595
595
596
596
int a[100];
597
- read(a,1000); // bad
597
+ read(a, 1000); // bad
598
598
599
599
better
600
600
601
601
void read(span<int> r); // read into the range of integers r
602
-
602
+
603
603
int a[100];
604
604
read(a); // better: let the compiler figure out the number of elements
605
605
@@ -988,7 +988,7 @@ Messy, low-level code breads more such code.
988
988
if (count==sz)
989
989
p = (int*) realloc(p,sizeof(int)*sz*2);
990
990
// ...
991
-
991
+
992
992
This is low-level, verbose, and error-prone.
993
993
Insted, we could use `vector`:
994
994
@@ -2687,7 +2687,7 @@ With C++11 we can write this, putting the results directly in existing local var
2687
2687
2688
2688
With C++17 we should be able to use "structured bindinds" to declare and initialize the multiple variables:
2689
2689
2690
- auto [iter,success] = myset.insert("Hello"); // C++17 structured binding
2690
+ auto [iter, success] = myset.insert("Hello"); // C++17 structured binding
2691
2691
if (success) do_something_with(iter);
2692
2692
2693
2693
##### Exception
@@ -2699,7 +2699,7 @@ For example:
2699
2699
2700
2700
istream& operator>>(istream& is, string& s); // much like std::operator>>()
2701
2701
2702
- for (string s; cin>> s; ) {
2702
+ for (string s; cin >> s; ) {
2703
2703
// do something with line
2704
2704
}
2705
2705
@@ -2712,11 +2712,11 @@ such as `string` and `vector`, that needs to do free store allocations.
2712
2712
2713
2713
To compare, if we passed out all values as return values, we would something like this:
2714
2714
2715
- pair<istream&,string> get_string(istream& is); // not recommended
2715
+ pair<istream&, string> get_string(istream& is); // not recommended
2716
2716
{
2717
2717
string s;
2718
- cin>> s;
2719
- return {is,s};
2718
+ cin >> s;
2719
+ return {is, s};
2720
2720
}
2721
2721
2722
2722
for (auto p = get_string(cin); p.first; ) {
@@ -2768,24 +2768,23 @@ It complicates checking and tool support.
2768
2768
2769
2769
##### Example
2770
2770
2771
- void use(int* p, int nchar * s, int* q)
2771
+ void use(int* p, int n, char * s, int* q)
2772
2772
{
2773
- p[n-1] = 666; // Bad: we don't know if p points to n elements; assume it does not or use span<int>
2774
-
2775
- cout << s; // Bad: we don't know if that s points to a zero-terminated array of char; // assume it does not or use zstring
2776
-
2777
- delete q; // Bad: we don't know if *q is allocated on the free store; assume it does not or use owner
2773
+ p[n-1] = 666; // Bad: we don't know if p points to n elements;
2774
+ // assume it does not or use span<int>
2775
+ cout << s; // Bad: we don't know if that s points to a zero-terminated array of char;
2776
+ // assume it does not or use zstring
2777
+ delete q; // Bad: we don't know if *q is allocated on the free store;
2778
+ //assume it does not or use owner
2778
2779
}
2779
2780
2780
2781
better
2781
2782
2782
2783
void use2(span<int> p, zstring s, owner<int*> q)
2783
2784
{
2784
- p[p.size()-1] = 666; // OK, a range error can be caught
2785
-
2786
- cout << s; // OK
2787
-
2788
- delete q; // OK
2785
+ p[p.size()-1] = 666; // OK, a range error can be caught
2786
+ cout << s; // OK
2787
+ delete q; // OK
2789
2788
}
2790
2789
2791
2790
##### Note
@@ -6150,16 +6149,16 @@ This Shape hierarchy can be rewritten using interface inheritance:
6150
6149
6151
6150
class Shape { // pure interface
6152
6151
public:
6153
- virtual Point center() const =0;
6154
- virtual Color color() const =0;
6152
+ virtual Point center() const = 0;
6153
+ virtual Color color() const = 0;
6155
6154
6156
- virtual void rotate(int) =0;
6157
- virtual void move(Point p) =0;
6155
+ virtual void rotate(int) = 0;
6156
+ virtual void move(Point p) = 0;
6158
6157
6159
- virtual void redraw() =0;
6158
+ virtual void redraw() = 0;
6160
6159
6161
6160
// ...
6162
- };
6161
+ };
6163
6162
6164
6163
Note that a pure interface rarely have constructors: there is nothing to construct.
6165
6164
@@ -6190,13 +6189,13 @@ First we devise a hierarchy of interface classes:
6190
6189
6191
6190
class Shape { // pure interface
6192
6191
public:
6193
- virtual Point center() const =0;
6194
- virtual Color color() const =0;
6192
+ virtual Point center() const = 0;
6193
+ virtual Color color() const = 0;
6195
6194
6196
- virtual void rotate(int) =0;
6197
- virtual void move(Point p) =0;
6195
+ virtual void rotate(int) = 0;
6196
+ virtual void move(Point p) = 0;
6198
6197
6199
- virtual void redraw() =0;
6198
+ virtual void redraw() = 0;
6200
6199
6201
6200
// ...
6202
6201
};
@@ -6258,7 +6257,7 @@ Since each implementation derived from its inteface as well as its implementatio
6258
6257
Smiley -> Circle -> Shape
6259
6258
^ ^ ^
6260
6259
| | |
6261
- Impl::Smiley -> Impl::Circle -> Impl::Shape
6260
+ Impl::Smiley -> Impl::Circle -> Impl::Shape
6262
6261
6263
6262
As mentioned, this is just one way to construct a dual hierarchy.
6264
6263
@@ -7479,9 +7478,12 @@ The default is the easiest to read and write.
7479
7478
7480
7479
##### Example
7481
7480
7482
- enum class Direction : char { n, s, e, w, ne, nw, se, sw }; // underlying type saves space
7481
+ enum class Direction : char { n, s, e, w,
7482
+ ne, nw, se, sw }; // underlying type saves space
7483
7483
7484
- enum class Webcolor : int { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF }; // underlying type is redundant
7484
+ enum class Webcolor : int { red = 0xFF0000,
7485
+ green = 0x00FF00,
7486
+ blue = 0x0000FF }; // underlying type is redundant
7485
7487
7486
7488
##### Note
7487
7489
@@ -7512,9 +7514,10 @@ The default gives a consequtive set of values that is good for `switch`-statemen
7512
7514
##### Example
7513
7515
7514
7516
enum class Col1 { red, yellow, blue };
7515
- enum class Col2 { red=1, red=2, blue=2 }; // typo
7516
- enum class Month { jan=1, feb, mar, apr, mar, jun, jul, august, sep, oct, nov, dec }; // starting with 1 is conventional
7517
- enum class Base_flag { dec=1, oct=dec<<1, hex=dec<<2 }; // set of bits
7517
+ enum class Col2 { red = 1, red = 2, blue = 2 }; // typo
7518
+ enum class Month { jan = 1, feb, mar, apr, mar, jun,
7519
+ jul, august, sep, oct, nov, dec }; // starting with 1 is conventional
7520
+ enum class Base_flag { dec = 1, oct = dec << 1, hex = dec << 2 }; // set of bits
7518
7521
7519
7522
Specifying values are neccessary to match conventional values (e.g., `Month`)
7520
7523
and where consecutive values are undesirable (e.g., to get separate bits as in `Base_flag`).
@@ -9416,29 +9419,29 @@ Requires messy cast-and-macro-laden code to get working right.
9416
9419
9417
9420
void error(int severity ...) // ``severity'' followed by a zero-terminated list of char*s; write the C-style strings to cerr
9418
9421
{
9419
- va_list ap; // a magic type for holding arguments
9420
- va_start(ap,severity); // arg startup: "severity" is the first argument of error()
9422
+ va_list ap; // a magic type for holding arguments
9423
+ va_start(ap, severity); // arg startup: "severity" is the first argument of error()
9421
9424
9422
- for (;;) {
9423
- char* p = va_arg(ap,char*); // treat the next var as a char*; no checking: a cast in disguise
9424
- if (p == nullptr) break;
9425
- cerr << p << ' ';
9426
- }
9425
+ for (;;) {
9426
+ char* p = va_arg(ap, char*); // treat the next var as a char*; no checking: a cast in disguise
9427
+ if (p == nullptr) break;
9428
+ cerr << p << ' ';
9429
+ }
9427
9430
9428
- va_end(ap); // arg cleanup (don't forget this)
9431
+ va_end(ap); // arg cleanup (don't forget this)
9429
9432
9430
- cerr << '\en ';
9431
- if (severity) exit(severity);
9433
+ cerr << '\n ';
9434
+ if (severity) exit(severity);
9432
9435
}
9433
9436
9434
9437
void use()
9435
9438
{
9436
- error(7,"this","is","an","error", nullptr);
9439
+ error(7, "this", "is", "an", "error", nullptr);
9437
9440
error(7); // crash
9438
- error(7,"this","is","an","error"); // crash
9441
+ error(7, "this", "is", "an", "error"); // crash
9439
9442
const char* is = "is";
9440
9443
string an = "an";
9441
- error(7,"this","is, an,"error"); // crash
9444
+ error(7, "this", "is", an, "error"); // crash
9442
9445
}
9443
9446
9444
9447
**Alternative**: Overloading. Templates. Variadic templates.
@@ -13448,10 +13451,10 @@ To say "`T` is `Sortable`":
13448
13451
// requires Sortable<T> // of type T which is the name of a type
13449
13452
void sort(T&); // that is Sortable"
13450
13453
13451
- template<Sortable T> // Better (assuming language support for concepts): "The parameter is of type T
13454
+ template<Sortable T> // Better (assuming support for concepts): "The parameter is of type T
13452
13455
void sort(T&); // which is Sortable"
13453
13456
13454
- void sort(Sortable&); // Best (assuming language support for concepts): "The parameter is Sortable"
13457
+ void sort(Sortable&); // Best (assuming support for concepts): "The parameter is Sortable"
13455
13458
13456
13459
The shorter versions better match the way we speak. Note that many templates don't need to use the `template` keyword.
13457
13460
@@ -13680,9 +13683,9 @@ An incomplete set of constraints can still be very useful:
13680
13683
13681
13684
// balancer for a generic binary tree
13682
13685
template<typename Node> concept bool Balancer = requires(Node* p) {
13683
- add_fixup(p);
13684
- touch(p);
13685
- detach(p);
13686
+ add_fixup(p);
13687
+ touch(p);
13688
+ detach(p);
13686
13689
}
13687
13690
13688
13691
So a `Balancer` must supply at least thee operations on a tree `Node`,
@@ -13736,7 +13739,7 @@ Two concepts requiring the same syntax but having different semantics leads to a
13736
13739
template<typename I> // iterator providing random access to contiguous data
13737
13740
concept bool Contiguous_iter =
13738
13741
RA_iter<I> && is_contiguous<I>::value; // using is_contiguous trait
13739
-
13742
+
13740
13743
The programmer (in a library) must define `is_contiguous` (a trait) appropriately.
13741
13744
13742
13745
Wrapping a tag class into a concept leads to a simpler expression of this idea:
@@ -13801,11 +13804,11 @@ The compiler will select the overload and emit an appropriate error.
13801
13804
Complementary constraints are unfortunately common in `enable_if` code:
13802
13805
13803
13806
template<typename T>
13804
- enable_if<!C<T>,void> // bad
13807
+ enable_if<!C<T>, void> // bad
13805
13808
f();
13806
13809
13807
13810
template<typename T>
13808
- enable_if<C<T>,void>
13811
+ enable_if<C<T>, void>
13809
13812
f();
13810
13813
13811
13814
@@ -13946,7 +13949,7 @@ we delay checking until instantiation time.
13946
13949
We consider this a worthwhile tradeoff.
13947
13950
13948
13951
Note that using non-local, non-dependent names (such as `debug` and `cerr`) also introduce context dependencies that may lead to "mysterious" errors.
13949
-
13952
+
13950
13953
##### Note
13951
13954
13952
13955
It can be hard to decide which properties of a type is essential and which are not.
@@ -14214,17 +14217,18 @@ Eases tool creation.
14214
14217
template<typename C>
14215
14218
void sort(C& c)
14216
14219
{
14217
- std::sort(begin(c),end(c)); // necessary and useful dependency
14220
+ std::sort(begin(c), end(c)); // necessary and useful dependency
14218
14221
}
14219
14222
14220
14223
template<typename Iter>
14221
14224
Iter algo(Iter first, Iter last) {
14222
- for (; first!= last; ++first) {
14225
+ for (; first != last; ++first) {
14223
14226
auto x = sqrt(*first); // potentially surprising dependency: which sqrt()?
14224
- helper(first,x); // potentially surprising dependency: heper is chosen based on first and x
14227
+ helper(first, x); // potentially surprising dependency:
14228
+ // helper is chosen based on first and x
14225
14229
TT var = 7; // potentially surprising dependency: which TT?
14226
14230
}
14227
- }
14231
+ }
14228
14232
14229
14233
##### Note
14230
14234
@@ -14385,10 +14389,10 @@ This is a simplified version of `std::copy` (ignoring the possibility of non-con
14385
14389
14386
14390
template<class T> struct copy_trait { using tag = non_pod_tag; }; // T is not "plain old data"
14387
14391
14388
- template<> struct copy_trait<int> { using tab = pod_tag; }; // int is "plain old data"
14392
+ template<> struct copy_trait<int> { using tab = pod_tag; }; // int is "plain old data"
14389
14393
14390
14394
template<class Iter>
14391
- Out copy_helper(Iter first, Iter last, Iter out, pog_tag )
14395
+ Out copy_helper(Iter first, Iter last, Iter out, pod_tag )
14392
14396
{
14393
14397
// use memmove
14394
14398
}
@@ -14402,13 +14406,13 @@ This is a simplified version of `std::copy` (ignoring the possibility of non-con
14402
14406
template<class Itert>
14403
14407
Out copy(Iter first, Iter last, Iter out)
14404
14408
{
14405
- return copy_helper(first,last,out, typename copy_trait<Iter>::tag{})
14409
+ return copy_helper(first, last, out, typename copy_trait<Iter>::tag{})
14406
14410
}
14407
14411
14408
14412
void use(vector<int>& vi, vector<int>& vi2, vector<string>& vs, vector<string>& vs2)
14409
14413
{
14410
- copy(vi.begin(),vi.end(), vi2.begin()); // uses memmove
14411
- copy(vs.begin(),vs.end(), vs2.begin()); // uses a loop calling copy constructors
14414
+ copy(vi.begin(), vi.end(), vi2.begin()); // uses memmove
14415
+ copy(vs.begin(), vs.end(), vs2.begin()); // uses a loop calling copy constructors
14412
14416
}
14413
14417
14414
14418
This is a general and powerful technique for compile-time algorithm selection.
@@ -14465,7 +14469,7 @@ When `concept`s become widely available such alternatives can be distinguished d
14465
14469
auto x = T(u); // construction or cast?
14466
14470
}
14467
14471
14468
- f(1,"asdf); // bad: cast from const char* to int
14472
+ f(1, "asdf" ); // bad: cast from const char* to int
14469
14473
14470
14474
##### Enforcement
14471
14475
@@ -14500,10 +14504,10 @@ There are three major ways to let calling code customize a template.
14500
14504
14501
14505
template<class T>
14502
14506
void test3(T t)
14503
- // Invoke a "trait"
14504
-
14505
- {
14506
- test_traits<T>::f(t); // require customizing test_traits<> to get non-default functions/types
14507
+ // Invoke a "trait"
14508
+ {
14509
+ test_traits<T>::f(t); // require customizing test_traits<>
14510
+ // to get non-default functions/types
14507
14511
}
14508
14512
14509
14513
A trait is usually a type alias to compute a type,
@@ -14930,15 +14934,16 @@ Documentation, readability, opportunity for reuse.
14930
14934
14931
14935
bool same(const Rec& a, const Rec& b)
14932
14936
{
14933
- return a.id== b.id;
14937
+ return a.id == b.id;
14934
14938
}
14935
14939
14936
14940
vector<Rec*> find_id(const string& name); // find all records for "name"
14937
14941
14938
- auto x = find_if(vr.begin(),vr.end(),
14942
+ auto x = find_if(vr.begin(), vr.end(),
14939
14943
[&](Rec& r) {
14940
- if (r.name.size()!=n.size()) return false; // name to compare to is in n
14941
- for (int i=0; i<r.name.size(); ++i) if (tolower(r.name[i])!=tolower(n[i])) return false;
14944
+ if (r.name.size() != n.size()) return false; // name to compare to is in n
14945
+ for (int i=0; i < r.name.size(); ++i)
14946
+ if (tolower(r.name[i]) != tolower(n[i])) return false;
14942
14947
return true;
14943
14948
}
14944
14949
);
0 commit comments