Posts A Tour of C++
Post
Cancel

A Tour of C++

Some things I wanted to note down from Bjame Stroustrup’s “A Tour of C++” book.

1.5 Types, Variables and Arithmetic

C++ offers a variety of notations for expressing initialization, such as the = used above, and a universal form based on curly-brace-delimited initializer lists:

1
2
3
4
5
double d1 = 2.3; // initialize d1 to 2.3
double d2 {2.3}; // initialize d2 to 2.3
complex<double> z2 {d1,d2};
complex<double> z3 = {1,2}; // the = is optional with { ... }
vector<int> v {1,2,3,4,5,6}; // a vector of ints

The = form is traditional and dates back to C, but if in doubt, use the general {}-list form. If nothing else, it saves you from conversions that lose information:

1
2
3
int i1 = 7.2; // i1 becomes 7 (surpr ise?)
int i2 {7.2}; // error : floating-point to integer conversion
int i3 = {7.2}; // error : floating-point to integer conversion (the = is redundant)

A constant cannot be left uninitialized and a variable should only be left uninitialized in extremely rare circumstances. Don’t introduce a name until you have a suitable value for it. This is something that is also told in the core guidelines - don’t separate declaration and initialization.

When defining a variable, you don’t actually need to state its type explicitly when it can be deduced from the initializer:

1
2
3
4
5
auto b = true; // a bool
auto ch = 'x'; // a char
auto i = 123; // an int
auto d = 1.2; // a double
auto z = sqrt(y); // z has the type of whatever sqr t(y) retur ns

With auto, we use the = because there is no potentially troublesome type conversion involved.

We use auto where we don’t hav e a specific reason to mention the type explicitly. ‘‘Specific reasons’’ include:

  • The definition is in a large scope where we want to make the type clearly visible to readers of our code.
  • We want to be explicit about a variable’s range or precision (e.g., double rather than float).

1.7 Constants

C++ supports two notions of immutability:

const: meaning roughly ‘‘I promise not to change this value.’’ This is used primarily to specify interfaces, so that data can be passed to functions without fear of it being modified. The compiler enforces the promise made by const.

constexpr: meaning roughly ‘‘to be evaluated at compile time.’’ This is used primarily to specify constants, to allow placement of data in read-only memory (where it is unlikely to be corrupted) and for performance.

1
2
3
4
5
6
7
8
9
const int dmv = 17; // dmv is a named constant
int var = 17; // var is not a constant
constexpr double max1 = 1.4square(dmv); // OK
constexpr double max2 = 1.4square(var); // error
const double max3 = 1.4square(var); // OK
double sum(const vector<double>&); // sum will not modify its argument
vector<double> v {1.2, 3.4, 4.5}; // v is not a constant
const double s1 = sum(v); // OK: evaluated at run time
constexpr double s2 = sum(v); // error : sum(v) not constant expression

To be constexpr, a function must be rather simple: just a return-statement computing a value. A constexpr function can be used for non-constant arguments, but when that is done the result is not a constant expression. We allow a constexpr function to be called with non-constant-expression arguments in contexts that do not require constant expressions, so that we don’t hav e to define essentially the same function twice: once for constant expressions and once for variables.

This post is licensed under CC BY 4.0 by the author.

Recent Update

    Contents