@ -465,7 +465,7 @@ portability will be impacted.
Using valid ISO C++ does not guarantee portability (let alone correctness).
Using valid ISO C++ does not guarantee portability (let alone correctness).
Avoid dependence on undefined behavior (e.g., [undefined order of evaluation ](#Res-order ))
Avoid dependence on undefined behavior (e.g., [undefined order of evaluation ](#Res-order ))
and be aware of constructs with implementation defined meaning (e.g., `sizeof(int)` ).
and be aware of constructs with implementation defined meaning (e.g., `sizeof(int)` ).
##### Note
##### Note
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.
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.
cerr << "Int too small\n"
cerr << "Int too small\n"
This example is easily simplified
This example is easily simplified
// Int is an alias used for integers
// Int is an alias used for integers
static_assert(sizeof(Int) >= 4); // do: compile-time check
static_assert(sizeof(Int) >= 4); // do: compile-time check
@ -594,12 +594,12 @@ This example is easily simplified
void read(int* p, int n); // read max n integers into *p
void read(int* p, int n); // read max n integers into *p
int a[100];
int a[100];
read(a,1000); // bad
read(a, 1000); // bad
better
better
void read(span< int > r); // read into the range of integers r
void read(span< int > r); // read into the range of integers r
int a[100];
int a[100];
read(a); // better: let the compiler figure out the number of elements
read(a); // better: let the compiler figure out the number of elements
@ -988,7 +988,7 @@ Messy, low-level code breads more such code.
if (count==sz)
if (count==sz)
p = (int*) realloc(p,sizeof(int)*sz*2);
p = (int*) realloc(p,sizeof(int)*sz*2);
// ...
// ...
This is low-level, verbose, and error-prone.
This is low-level, verbose, and error-prone.
Insted, we could use `vector` :
Insted, we could use `vector` :
@ -2687,7 +2687,7 @@ With C++11 we can write this, putting the results directly in existing local var
With C++17 we should be able to use "structured bindinds" to declare and initialize the multiple variables:
With C++17 we should be able to use "structured bindinds" to declare and initialize the multiple variables:
auto [iter,success] = myset.insert("Hello"); // C++17 structured binding
auto [iter, success] = myset.insert("Hello"); // C++17 structured binding
if (success) do_something_with(iter);
if (success) do_something_with(iter);
##### Exception
##### Exception
@ -2699,7 +2699,7 @@ For example:
istream& operator>>(istream& is, string& s); // much like std::operator>>()
istream& operator>>(istream& is, string& s); // much like std::operator>>()
for (string s; cin>>s; ) {
for (string s; cin >> s; ) {
// do something with line
// do something with line
}
}
@ -2712,11 +2712,11 @@ such as `string` and `vector`, that needs to do free store allocations.
To compare, if we passed out all values as return values, we would something like this:
To compare, if we passed out all values as return values, we would something like this:
pair< istream & , string > get_string(istream& is); // not recommended
pair< istream & , string > get_string(istream& is); // not recommended
{
{
string s;
string s;
cin>>s;
cin >> s;
return {is,s};
return {is, s};
}
}
for (auto p = get_string(cin); p.first; ) {
for (auto p = get_string(cin); p.first; ) {
@ -2768,24 +2768,23 @@ It complicates checking and tool support.
##### Example
##### Example
void use(int* p, int nchar* s, int* q)
void use(int* p, int n, char* s, int* q)
{
{
p[n-1] = 666; // Bad: we don't know if p points to n elements; assume it does not or use span< int >
p[n-1] = 666; // Bad: we don't know if p points to n elements;
// assume it does not or use span< int >
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
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
delete q; // Bad: we don't know if *q is allocated on the free store; assume it does not or use owner
delete q; // Bad: we don't know if *q is allocated on the free store;
//assume it does not or use owner
}
}
better
better
void use2(span< int > p, zstring s, owner< int * > q)
void use2(span< int > p, zstring s, owner< int * > q)
{
{
p[p.size()-1] = 666; // OK, a range error can be caught
p[p.size()-1] = 666; // OK, a range error can be caught
cout < < s ; / / OK
cout < < s ; / / OK
delete q; // OK
delete q; // OK
}
}
##### Note
##### Note
@ -6150,16 +6149,16 @@ This Shape hierarchy can be rewritten using interface inheritance:
class Shape { // pure interface
class Shape { // pure interface
public:
public:
virtual Point center() const =0;
virtual Point center() const = 0;
virtual Color color() const =0;
virtual Color color() const = 0;
virtual void rotate(int) =0;
virtual void rotate(int) = 0;
virtual void move(Point p) =0;
virtual void move(Point p) = 0;
virtual void redraw() =0;
virtual void redraw() = 0;
// ...
// ...
};
};
Note that a pure interface rarely have constructors: there is nothing to construct.
Note that a pure interface rarely have constructors: there is nothing to construct.
@ -6190,13 +6189,13 @@ First we devise a hierarchy of interface classes:
class Shape { // pure interface
class Shape { // pure interface
public:
public:
virtual Point center() const =0;
virtual Point center() const = 0;
virtual Color color() const =0;
virtual Color color() const = 0;
virtual void rotate(int) =0;
virtual void rotate(int) = 0;
virtual void move(Point p) =0;
virtual void move(Point p) = 0;
virtual void redraw() =0;
virtual void redraw() = 0;
// ...
// ...
};
};
@ -6258,7 +6257,7 @@ Since each implementation derived from its inteface as well as its implementatio
Smiley -> Circle -> Shape
Smiley -> Circle -> Shape
^ ^ ^
^ ^ ^
| | |
| | |
Impl::Smiley -> Impl::Circle -> Impl::Shape
Impl::Smiley -> Impl::Circle -> Impl::Shape
As mentioned, this is just one way to construct a dual hierarchy.
As mentioned, this is just one way to construct a dual hierarchy.
@ -7479,9 +7478,12 @@ The default is the easiest to read and write.
##### Example
##### Example
enum class Direction : char { n, s, e, w, ne, nw, se, sw }; // underlying type saves space
enum class Direction : char { n, s, e, w,
ne, nw, se, sw }; // underlying type saves space
enum class Webcolor : int { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF }; // underlying type is redundant
enum class Webcolor : int { red = 0xFF0000,
green = 0x00FF00,
blue = 0x0000FF }; // underlying type is redundant
##### Note
##### Note
@ -7512,9 +7514,10 @@ The default gives a consequtive set of values that is good for `switch`-statemen
##### Example
##### Example
enum class Col1 { red, yellow, blue };
enum class Col1 { red, yellow, blue };
enum class Col2 { red=1, red=2, blue=2 }; // typo
enum class Col2 { red = 1, red = 2, blue = 2 }; // typo
enum class Month { jan=1, feb, mar, apr, mar, jun, jul, august, sep, oct, nov, dec }; // starting with 1 is conventional
enum class Month { jan = 1, feb, mar, apr, mar, jun,
enum class Base_flag { dec=1, oct=dec< < 1 , hex = dec<<2 } ; / / set of bits
jul, august, sep, oct, nov, dec }; // starting with 1 is conventional
enum class Base_flag { dec = 1, oct = dec < < 1 , hex = dec < < 2 } ; / / set of bits
Specifying values are neccessary to match conventional values (e.g., `Month` )
Specifying values are neccessary to match conventional values (e.g., `Month` )
and where consecutive values are undesirable (e.g., to get separate bits as in `Base_flag` ).
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.
void error(int severity ...) // ``severity'' followed by a zero-terminated list of char*s; write the C-style strings to cerr
void error(int severity ...) // ``severity'' followed by a zero-terminated list of char*s; write the C-style strings to cerr
{
{
va_list ap; // a magic type for holding arguments
va_list ap; // a magic type for holding arguments
va_start(ap,severity); // arg startup: "severity" is the first argument of error()
va_start(ap, severity); // arg startup: "severity" is the first argument of error()
for (;;) {
for (;;) {
char* p = va_arg(ap, char*); // treat the next var as a char*; no checking: a cast in disguise
char* p = va_arg(ap, char*); // treat the next var as a char*; no checking: a cast in disguise
if (p == nullptr) break;
if (p == nullptr) break;
cerr < < p < < ' ' ;
cerr < < p < < ' ' ;
}
}
va_end(ap); // arg cleanup (don't forget this)
va_end(ap); // arg cleanup (don't forget this)
cerr << '\e n';
cerr << '\ n';
if (severity) exit(severity);
if (severity) exit(severity);
}
}
void use()
void use()
{
{
error(7,"this","is","an","error", nullptr);
error(7, "this", "is", "an", "error", nullptr);
error(7); // crash
error(7); // crash
error(7,"this","is","an","error"); // crash
error(7, "this", "is", "an", "error"); // crash
const char* is = "is";
const char* is = "is";
string an = "an";
string an = "an";
error(7,"this","is,an,"error"); // crash
error(7, "this", "is" , an, "error"); // crash
}
}
**Alternative**: Overloading. Templates. Variadic templates.
**Alternative**: Overloading. Templates. Variadic templates.
@ -13511,10 +13514,10 @@ To say "`T` is `Sortable`":
// requires Sortable< T > // of type T which is the name of a type
// requires Sortable< T > // of type T which is the name of a type
void sort(T&); // that is Sortable"
void sort(T&); // that is Sortable"
template< Sortable T > // Better (assuming language support for concepts): "The parameter is of type T
template< Sortable T > // Better (assuming support for concepts): "The parameter is of type T
void sort(T&); // which is Sortable"
void sort(T&); // which is Sortable"
void sort(Sortable&); // Best (assuming language support for concepts): "The parameter is Sortable"
void sort(Sortable&); // Best (assuming support for concepts): "The parameter is Sortable"
The shorter versions better match the way we speak. Note that many templates don't need to use the `template` keyword.
The shorter versions better match the way we speak. Note that many templates don't need to use the `template` keyword.
@ -13743,9 +13746,9 @@ An incomplete set of constraints can still be very useful:
// balancer for a generic binary tree
// balancer for a generic binary tree
template< typename Node > concept bool Balancer = requires(Node* p) {
template< typename Node > concept bool Balancer = requires(Node* p) {
add_fixup(p);
add_fixup(p);
touch(p);
touch(p);
detach(p);
detach(p);
}
}
So a `Balancer` must supply at least thee operations on a tree `Node` ,
So a `Balancer` must supply at least thee operations on a tree `Node` ,
@ -13799,7 +13802,7 @@ Two concepts requiring the same syntax but having different semantics leads to a
template< typename I > // iterator providing random access to contiguous data
template< typename I > // iterator providing random access to contiguous data
concept bool Contiguous_iter =
concept bool Contiguous_iter =
RA_iter< I > & & is_contiguous< I > ::value; // using is_contiguous trait
RA_iter< I > & & is_contiguous< I > ::value; // using is_contiguous trait
The programmer (in a library) must define `is_contiguous` (a trait) appropriately.
The programmer (in a library) must define `is_contiguous` (a trait) appropriately.
Wrapping a tag class into a concept leads to a simpler expression of this idea:
Wrapping a tag class into a concept leads to a simpler expression of this idea:
@ -13864,11 +13867,11 @@ The compiler will select the overload and emit an appropriate error.
Complementary constraints are unfortunately common in `enable_if` code:
Complementary constraints are unfortunately common in `enable_if` code:
template< typename T >
template< typename T >
enable_if<!C<T> ,void> // bad
enable_if<!C<T> , void> // bad
f();
f();
template< typename T >
template< typename T >
enable_if< C < T > ,void>
enable_if< C < T > , void>
f();
f();
@ -14009,7 +14012,7 @@ we delay checking until instantiation time.
We consider this a worthwhile tradeoff.
We consider this a worthwhile tradeoff.
Note that using non-local, non-dependent names (such as `debug` and `cerr` ) also introduce context dependencies that may lead to "mysterious" errors.
Note that using non-local, non-dependent names (such as `debug` and `cerr` ) also introduce context dependencies that may lead to "mysterious" errors.
##### Note
##### Note
It can be hard to decide which properties of a type is essential and which are not.
It can be hard to decide which properties of a type is essential and which are not.
@ -14277,17 +14280,18 @@ Eases tool creation.
template< typename C >
template< typename C >
void sort(C& c)
void sort(C& c)
{
{
std::sort(begin(c),end(c)); // necessary and useful dependency
std::sort(begin(c), end(c)); // necessary and useful dependency
}
}
template< typename Iter >
template< typename Iter >
Iter algo(Iter first, Iter last) {
Iter algo(Iter first, Iter last) {
for (; first!=last; ++first) {
for (; first != last; ++first) {
auto x = sqrt(*first); // potentially surprising dependency: which sqrt()?
auto x = sqrt(*first); // potentially surprising dependency: which sqrt()?
helper(first,x); // potentially surprising dependency: heper is chosen based on first and x
helper(first, x); // potentially surprising dependency:
// helper is chosen based on first and x
TT var = 7; // potentially surprising dependency: which TT?
TT var = 7; // potentially surprising dependency: which TT?
}
}
}
}
##### Note
##### Note
@ -14448,10 +14452,10 @@ This is a simplified version of `std::copy` (ignoring the possibility of non-con
template< class T > struct copy_trait { using tag = non_pod_tag; }; // T is not "plain old data"
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< > struct copy_trait< int > { using tab = pod_tag; }; // int is "plain old data"
template< class Iter >
template< class Iter >
Out copy_helper(Iter first, Iter last, Iter out, pog _tag)
Out copy_helper(Iter first, Iter last, Iter out, pod _tag)
{
{
// use memmove
// use memmove
}
}
@ -14465,13 +14469,13 @@ This is a simplified version of `std::copy` (ignoring the possibility of non-con
template< class Itert >
template< class Itert >
Out copy(Iter first, Iter last, Iter out)
Out copy(Iter first, Iter last, Iter out)
{
{
return copy_helper(first,last,out, typename copy_trait< Iter > ::tag{})
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)
void use(vector< int > & vi, vector< int > & vi2, vector< string > & vs, vector< string > & vs2)
{
{
copy(vi.begin(),vi.end(), vi2.begin()); // uses memmove
copy(vi.begin(), vi.end(), vi2.begin()); // uses memmove
copy(vs.begin(),vs.end(), vs2.begin()); // uses a loop calling copy constructors
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.
This is a general and powerful technique for compile-time algorithm selection.
@ -14528,7 +14532,7 @@ When `concept`s become widely available such alternatives can be distinguished d
auto x = T(u); // construction or cast?
auto x = T(u); // construction or cast?
}
}
f(1,"asdf); // bad: cast from const char* to int
f(1, "asdf" ); // bad: cast from const char* to int
##### Enforcement
##### Enforcement
@ -14563,10 +14567,10 @@ There are three major ways to let calling code customize a template.
template< class T >
template< class T >
void test3(T t)
void test3(T t)
// Invoke a "trait"
// Invoke a "trait"
{
{
test_traits< T > ::f(t); // require customizing test_traits< >
test_traits< T > ::f(t); // require customizing test_traits< > to get non-default functions/types
// to get non-default functions/types
}
}
A trait is usually a type alias to compute a type,
A trait is usually a type alias to compute a type,
@ -14993,15 +14997,16 @@ Documentation, readability, opportunity for reuse.
bool same(const Rec& a, const Rec& b)
bool same(const Rec& a, const Rec& b)
{
{
return a.id==b.id;
return a.id == b.id;
}
}
vector< Rec * > find_id(const string& name); // find all records for "name"
vector< Rec * > find_id(const string& name); // find all records for "name"
auto x = find_if(vr.begin(),vr.end(),
auto x = find_if(vr.begin(), vr.end(),
[& ](Rec& r ) {
[& ](Rec& r ) {
if (r.name.size()!=n.size()) return false; // name to compare to is in n
if (r.name.size() != n.size()) return false; // name to compare to is in n
for (int i=0; i< r.name.size ( ) ; + + i ) if ( tolower ( r . name [ i ] ) ! = tolower ( n [ i ] ) ) return false ;
for (int i=0; i < r.name.size ( ) ; + + i )
if (tolower(r.name[i]) != tolower(n[i])) return false;
return true;
return true;
}
}
);
);