Standards

C++11

  • see p. xix “New Features in C++11”
  • list of all N#### papers of the WG21 C++ Standards Committee: list
    • eg. 2022 N4903-N4927 indicates that the paper numbers N4903 to N4927 can be found in the listed mailings under year 2022

Definitions

Entity

From cppreference:

The entities of a C++ program are

  • values,
  • objects,
  • references,
  • structured bindings (since C++17),
  • functions,
  • enumerators,
  • types,
  • class members,
  • templates,
  • template specializations,
  • parameter packs (since C++11), and
  • namespaces.

Note: Preprocessor macros are not C++ entities.

Declaration

#include <type_traits>
 
struct S
{
    int member;
    // decl-specifier-seq is "int"
    // declarator is "member"
} obj, *pObj(&obj);
// decl-specifier-seq is "struct S { int member; }"
// declarator "obj" declares an object of type S
// declarator "*pObj" declares a pointer to S,
//     and initializer "(&obj)" initializes it
 
int i = 1, *p = nullptr, f(), (*pf)(double);
// decl-specifier-seq is "int"
// declarator "i" declares a variable of type int,
//     and initializer "= 1" initializes it
// declarator "*p" declares a variable of type int*,
//     and initializer "= nullptr" initializes it
// declarator "f()" declares (but doesn't define)
//     a function taking no arguments and returning int
// declarator "(*pf)(double)" declares a pointer to function
//     taking double and returning int

Details:

“A simple declaration is a statement that introduces, creates, and optionally initializes one or several identifiers, typically variables.”

decl-specifier-seq init-declarator-list(optional);

decl-specifier-seq is a sequence of the following whitespace-separated specifiers, in any order:”

  • type specifier: int, double, class specifier, cv qualifier, etc.
  • typedef
  • inline, virtual, explicit
  • friend
  • constexpr
  • storage class specifier: register, static, thread_local, extern, mutable
  • … etc.

init-declarator-list is a comma-separated sequence of one or more init-declarators, which have the following syntax:”

declarator initializer(optional)

“Each declarator (a part of an init-declarator-list, see code block above) introduces exactly one object, reference, function, or (for typedef declarations) type alias, whose type is provided by decl-specifier-seq and optionally modified by operators such as & (reference to) or [] (array of) or () (function returning) in the declarator.”

Deep Copy vs Shallow Copy

see stackoverflow

Areas of Memory

  • stack - Local variables, function parameters
  • code space - Code
  • global namespace - global variables
  • registers - used for internal housekeeping functions, such as keeping track of the top of the stack and the instruction pointer
  • free store = heap = dynamic memory - Just about all of the remaining memory

Value Categories

Just learn them by using this list of examples.

From learn.microsoft.com:

  • Every C++ expression has a type, and belongs to a value category.
  • The value categories are the basis for rules that compilers must follow when creating, copying, and moving temporary objects during expression evaluation.
  • C++17:
    • A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function.
    • A prvalue is an expression whose evaluation initializes an object or a bit-field, or computes the value of the operand of an operator, as specified by the context in which it appears.
      • has no address that is accessible by your program
      • examples
        • literals,
        • function calls that return a nonreference type,
          • eg. str.substr(1, 2), str1 + str2, or it++
        • temporary objects that are created during expression evaluation but accessible only by the compiler
        • &a (address-of expression)
        • a++ and a--
        • a + b, a % b, a & b, a << b, (built-in arithmetic expressions)
    • An xvalue is a glvalue that denotes an object or bit-field whose resources can be reused (usually because it is near the end of its lifetime).
      • has an address that no longer accessible by your program but can be used to initialize an rvalue reference, which provides access to the expression
      • examples
        • Certain kinds of expressions involving rvalue references (8.3.2) yield xvalues, such as a call to a function whose return type is an rvalue reference or a cast to an rvalue reference type.
        • function calls that return an rvalue reference, and
        • the array subscript expressions where the array is an rvalue reference
        • member and pointer to member expressions where the object is an rvalue reference
    • An lvalue is a glvalue that isn’t an xvalue.
      • has an address that your program can access
      • examples
        • variable names, including const variables,
        • array elements,
        • function calls that return an lvalue reference,
        • bit-fields,
        • unions, and
        • class members
        • *p (indirection expression)
        • a[n] (subscript expression)
        • ++a and --a
        • a = b, a += b, a %= b (assignment and compound assignment expression)
    • An rvalue is a prvalue or an xvalue.

value-categories

stackoverflow:

  • “The whole massacre began with the move semantics.”

lvalue and rvalue (informal)

  • every expression in C++ is either an lvalue or an rvalue
  • inherited from C
    • in C: simple mnemonic purpose: lvalues could stand on the left-hand side of an assignment whereas rvalues could not
  • in C++: Roughly speaking,
    • when we use an object as an rvalue, we use the object’s value (its contents).
    • when we use an object as an lvalue, we use the object’s identity (its location in memory).
  • we can use an lvalue when an rvalue is required, but we cannot use an rvalue when an lvalue (i.e., a location) is required

POD Types (for Layout Dependent Operations)

phth:

  • Some programs and compilers use operations that depend on a particular memory layout.
  • Therefore, C++14 introduced three categories of simple classes and structs,
    • trivial
    • standard-layout
    • POD
  • By checking these categories programs can check the suitability of any given type for operations that depend on a particular memory layout.

Layout

learn.microsoft.com:

  • layout: refers to how the members of an object of class, struct or union type are arranged in memory.
    • In some cases, the layout is well-defined by the language specification.
    • But when a class or struct contains certain C++ language features such as virtual base classes, virtual functions, members with different access control, then the compiler is free to choose a layout.
      • That layout may vary depending on what optimizations are being performed and in many cases the object might not even occupy a contiguous area of memory.
  • POD type: When a class or struct is both trivial and standard-layout, it is a POD (Plain Old Data) type.
    • The memory layout of POD types is therefore contiguous and each member has a higher address than the member that was declared before it, so that byte for byte copies and binary I/O can be performed on these types.
    • Scalar types such as int are also POD types.
    • POD types that are classes can have only POD types as non-static data members.

PODType (“Plain Old Data” Type)

  • “This means the type is compatible with the types used in the C programming language, that is, can be exchanged with C libraries directly, in its binary form.” (cppreference)
    1. scalar types
      • “object types that are not array types or class types”, → named requirements
      • “The following types are collectively called scalar types:” (cppreference)
      • arithmetic types
      • enumeration types
      • pointer types
      • pointer-to-member types
      • std::nullptr_t
      • cv-qualified versions of these types
    2. POD classes
    3. arrays of such types
    4. cv-qualified versions of these types
  • in particular, if a type is a PODType, this means that the type is compatible with C structs
  • details (including historic changes):
  • informal:

Operators

  • the scope operator “::
  • left-shift operator “<<” (bitwise), cppreference
  • the output operator “<<
    • returns its left-hand operand
  • right-shift operator “>>” (bitwise), cppreference
  • the input operator “>>
    • returns its left-hand operand
  • assignment operators (=)
    • requires a (nonconst, ie. modifiable) lvalue as its left-hand operand
    • yields its left-hand operand as an lvalue
    • sub-categories:
      • copy assignment operator
      • move assignment operator
      • compound assignment operator (+=)
  • arithmetic operators
    • addition operator (+)
    • shift operators “<<” and “>>” (bitwise)
  • logical and relational operators
    • logical AND (&&)
    • less than (<)
  • the call operator (())
  • increment operator “++” and decrement operator “--
    • Pre-increment and pre-decrement operators
      • increments or decrements the value of the object and
      • returns a reference to the result.
        • an lvalue expression that identifies the modified operand
      • the expression ++x is exactly equivalent to x += 1
      • the expression --x is exactly equivalent to x -= 1
    • Post-increment and post-decrement
      • creates a copy of the object,
      • increments or decrements the value of the object and
      • returns the copy from before the increment or decrement.
        • result is prvalue copy of the original value of the operand.
      • the expression x++ modifies the value of its operand as if by evaluating x += 1
      • the expression x-- modifies the value of its operand as if by evaluating x -= 1
  • complement operator (~)
  • member access operators
    • subscript operator (a[b])
      • “provides access to an object pointed-to by the pointer or array operand”
    • dereference/indirection operator (*a)
      • “provides access to an object or function pointed-to by the pointer operand”
      • converts a pointer value to an lvalue, (learn.microsoft.com)
      • The operand of the indirection operator must be a pointer to a type, (learn.microsoft.com)
      • result of the indirection expression is the type from which the pointer type is derived, (learn.microsoft.com)
        • eg. if the operand is of type char*, the result of the expression is of type char
    • address-of operator (&a)
      • “creates a pointer pointing to the object or function operand”
      • requires an lvalue operand
      • returns a pointer to its operand as a prvalue
      • result of the expression is a pointer type (an prvalue) derived from the type of the operand, (learn.microsoft.com)
        • eg. if the operand is of type char, the result of the expression is of type char* (pointer to char)
    • member of object/dot operator (a.b)
      • “provide access to a data member or member function of the object operand”
      • returns the member named by the right-hand operand
    • member of pointer (a->b, equivalent to (*a).b)
      • “provide access to a data member or member function of the class pointed-to by the pointer operand”

Lvalues vs Rvalues and Operators

  • Operators differ as to whether they
    • require lvalue or rvalue operands
    • return lvalues or rvalues
  • Examples of operators that involve lvalues:
    • Assignment
      • requires a (nonconst) lvalue as its left-hand operand
      • yields its left-hand operand as an lvalue.
    • address-of operator
      • requires an lvalue operand
      • returns a pointer to its operand as an rvalue.
    • built-in dereference and subscript operators and the iterator dereference and string and vector subscript operators
      • all yield lvalues.
    • built-in and iterator increment and decrement operators
      • require lvalue operands
      • the prefix versions (which are the ones used so far) also yield lvalues.

Comma Operator

cppreference:

The comma operator expressions have the form

E1 , E2

In a comma expression E1, E2, the expression E1 is evaluated, its result is discarded (although if it has class type, it won’t be destroyed until the end of the containing full expression), and its side effects are completed before evaluation of the expression E2 begins.

The type, value, and value category of the result of the comma expression are exactly the type, value, and value category of the second operand, E2.

Associativity:

From stackoverflow:

“the , operator in C++ is associative. It doesn’t matter which way you parenthesize the expressions. ((a, b), c) and (a, (b, c)) both evaluate and discard a, then evaluate and discard b, then evaluate c and that’s the result.”

Expressions, Literals

source: https://en.cppreference.com/w/cpp/language/expressions

  • expression := sequence of operators and their operands, that specifies a computation.
  • primary expression := literals, id-expressions, lambda-expressions, fold-expressions, requires-expressions
    • literals := the tokens of a C++ program that represent constant values ( = Konstanten ) (dh alle Zeichen, die man “nicht ändern kann”)
      • integer literals (eg 1, 2, 3, …)
      • character literals (eg einzelne Buchstaben)
      • boolean literals (true und false)
      • nullptr (Merke: nullptr ist ein literal!)
  • C++ expression properties: “each C++ expression is characterized by two independent properties
    • 1. type
    • 2. value category: (see Value Categories)
      • Primary categories:
        • prvalue expression
          • literals (s.o.)
          • arithmetic expressions (+,-,%,…)
        • xvalue expression
        • lvalue expression
      • Mixed categories:
        • glvalue expression
        • rvalue expression
  • full-expression: “A full expression is an expression that is not part of another expression or of a declarator.”, source

Statements

  • “fragments of the C program that are executed in sequence”
  • 5 types
    • 1) compound statements (aka block)
      • “a brace-enclosed sequence of statements and declarations.”
      • “Each compound statement introduces its own block scope.”
    • 2) expression statements
      • “An expression followed by a semicolon is a statement.”
    • 3) selection statements
    • 4) iteration statements
    • 5) jump statements

cppreference:

Statements are fragments of the C++ program that are executed in sequence.

  • The body of any function is a sequence of statements.
  • For example:
int main()
{
    int n = 1;                        // declaration statement
    n = n + 1;                        // expression statement
    std::cout << "n = " << n << '\n'; // expression statement
    return 0;                         // return statement
}

C++ includes the following types of statements:

  1. labeled statements;
  2. expression statements;
    • “An expression statement is an expression followed by a semicolon.”
    • syntax: attr(optional) expression(optional);
    • “Most statements in a typical C++ program are expression statements, such as assignments or function calls.”
    • “An expression statement without an expression is called a null statement. It is often used to provide an empty body to a for or while loop.”
  3. compound statements;
  4. selection statements;
  5. iteration statements;
  6. jump statements;
  7. declaration statements;
  8. try blocks;
  9. atomic and synchronized blocks (TM TS).

CV Type Qualifiers

  • “Appear in any type specifier, including decl-specifier-seq of declaration grammar, to specify constness or volatility of the object being declared or of the type being named.”
    • const - defines that the type is constant.
    • volatile - defines that the type is volatile.

Wikipedia:

As of 2014 and C11, there are four type qualifiers in standard C: const (C89), volatile (C89), restrict (C99) and _Atomic (C11) – the latter has a private name to avoid clashing with user-defined names. The first two of these, const and volatile, are also present in C++, and are the only type qualifiers in C++. Thus in C++ the term “cv-qualified type” (for const and volatile) is often used for “qualified type”, while the terms “c-qualified type” and “v-qualified type” are used when only one of the qualifiers is relevant.

Of these, const is by far the best-known and most used, appearing in the C and C++ standard libraries and encountered in any significant use of these languages, which must satisfy const-correctness. The other qualifiers are used for low-level programming, and while widely used there, are rarely used by typical programmers. For a time however volatile was used by some C++ programmers for synchronization during threading, though this was discouraged and is now broken in most compilers.

Factory

Wikipedia:

  • In object-oriented programming, a factory is an object for creating other objects;
  • formally, it is a function or method that returns objects of a varying prototype or class from some method call, which is assumed to be “new”.
  • More broadly, a subroutine that returns a “new” object may be referred to as a “factory”, as in factory method or factory function. (“even if functions are not objects”)
  • The factory pattern is the basis for a number of related software design patterns.

Invariants

BS4.3

  • “invariant” aka “class invariant”
  • “a statement of what is assumed to be true for a class”
    • eg. for Vector, the statement “elem points to an array of sz doubles” is an invariant
      • nothing Vector does makes any sense unless the members of Vector have reasonable values
  • job of a constructor: establish the invariant for its class (so that the member functions can rely on it)
  • job of the member functions: make sure that the invariant holds when the member functions exit

Parameterized Types

“C# Generics and C++ templates are both language features that provide support for parameterized types. However, there are many differences between the two.”, learn.microsoft.com

templates provide compile-time parameterization”, learn.microsoft.com

Side Effects

cppreference:

Evaluation of Expressions

Evaluation of each expression includes:

  • Value computations: calculation of the value that is returned by the expression. This may involve determination of the identity of the object (glvalue evaluation, e.g. if the expression returns a reference to some object) or reading the value previously assigned to an object (prvalue evaluation, e.g. if the expression returns a number, or some other value).
  • Initiation of side effects: access (read or write) to an object designated by a volatile glvalue, modification (writing) to an object, calling a library I/O function, or calling a function that does any of those operations.

Header Files

Using C Headers in C++

  • use the C++ versions of C library headers
    • Headers in C have names of the form name.h. The C++ versions of these headers are named cname.
    • Warning: names defined in the cname headers are defined inside the std namespace, whereas those defined in the .h versions are not

Newline at EOF

It is good style to always put a newline at the end of text files (POSIX Standard), stackoverflow

  • this is part of language standards:
    • in C: newline required at the end of header files
    • in C++: before C++11: newline required at the end of header files
    • No newline at end of file” compiler warning, stackoverflow
  • in git: stackoverflow

Include Syntax

Usual practice is to use the #include "local.h" form for headers inside a library/package/module, and the #include <external.h> form for headers from external/3rd-party or system libraries.

Lippman, Lajoie, Moo: Headers from the standard library are enclosed in angle brackets (< >). Those that are not part of the library are enclosed in double quotes (" ").

Keywords

  • see Table 2.3

using

Using the namespace designation is good form because, if you use using namespace some_namespace, you run the risk of inadvertently using objects from the wrong library. This takes some effort to fix, whereas using the namespace designation cannot lead to such conflicts (see example). I.e. from a maintenance perspective using the namespace designation is best practice.

= default

  • = default can be used only on member functions that have a synthesized version
  • force the automatic generation” (cppreference)
  • defaulted functions:
    • explicitly-defaulted function definition: as an explicit instruction to the compiler to generate a special member function
  • to explicitly ask the compiler to generate the synthesized versions of the copy-control members
  • the synthesized function is implicitly inline (just as is any other member function defined in the body of the class)
    • if we do not want the synthesized member to be an inline function, we can specify = default on the member’s definition (outside the class)

= delete

  • Motivation: how to prevent copies?
    • simply not defining the copy-control members does not work because the compiler will synthesize them, if we do not define them
  • deleted functions:
    • one that is declared but may not be used in any other way
  • signals to the compiler (and to readers of our code) that we are intentionally not defining these members
  • A =delete makes an attempted use of the deleted function a compile-time error (BS6.1.1)
  • Unlike = default, = delete
    • must appear on the first declaration of a deleted function
    • we can specify = delete on any function
      • sometimes also useful to guide the function-matching process
  • use cases:
    • “A base class in a class hierarchy is the classic example where we don’t want to allow a memberwise copy” (BS6.1.1)
  • Warning: You can delete a destructor. However, it is not possible to
    • define an object of a type with a deleted destructor
    • delete a pointer to a dynamically allocated object of a type with a deleted destructor
      • ie you can dynamically allocate such objects, but you cannot free them
  • “synthesized as deleted”
    • in essence, if a class has a data member that cannot be default constructed, copied, assigned, or destroyed, then the corresponding copy-control member will be a deleted function
  • prior to the new standard:
    • prevent copying by declaring the copy constructor and copy-assignment operator as private
      • not best practice! Use = default instead.

const

  • top-level const: indicates that an variable/object itself is const
  • low-level const: appears in the base type of compound types such as pointers or references
int i = 0;
int *const p1 = &i;       // we can't change the value of p1; const is top-level
const int ci = 42;        // we cannot change ci; const is top-level
const int *p2 = &ci;      // we can change p2; const is low-level
const int *const p3 = p2; // right-most const is top-level, left-most is not
const int &r = ci;        // const in reference types is always low-level
  • When we copy an object top-level consts are ignored
i = ci;   // ok: copying the value of ci; top-level const in ci is ignored
p2 = p3;  // ok: pointed-to type matches; top-level const in p3 is ignored
  • low-level consts are never ignored (ie. both objects must have the same low-level const qualification)
    • In general, we can convert a nonconst to const but not the other way round
int *p = p3;        // error: p3 has a low-level const but p doesn't
p2 = p3;            // ok: p2 has the same low-level const qualification as p3
p2 = &i;            // ok: we can convert int* to const int*
// see "references.md" -> "reference to const"
int &r = ci;        // error: can't bind an ordinary int& to a const int object
const int &r2 = i;  // ok: can bind const int& to plain int

Guidelines

  • you should not declare member variables as const, stackoverflow
    • const member variables usually serve no purpose
  • For a class type, if a piece of data is not supposed to change after being initialized, enforce this by design of the public API, not via the const key word., reddit
  • const fields are often not necessary - it is enough to pass a const object (or a pointer/reference to a const object), isocpp

Constant Expressions (C++11)

constant expressions:

  • an expression
    • whose value cannot change and
    • that can be evaluated at compile time.
  • examples:
    • A literal
    • A const object that is initialized from a constant expression
const int max_files = 20;         // max_files is a constant expression
const int limit = max_files + 1;  // limit is a constant expression
int staff_size = 27;              // staff_size is not a constant expression
const int sz = get_size();        // sz is not a constant expression (because value of its initializer is not known until run time)

constexpr Declaration

  • since C++11
    • introduced because it can be difficult to determine that an initializer is a constant expression
  • asks the compiler to verify that a variable is a constant expression
  • Variables declared as constexpr are
    • implicitly const and
    • must be initialized by constant expressions
  • cannot use an ordinary function as an initializer for a constexpr variable, but we can use constexpr functions
  • best practice:
    • use constexpr for variables that you intend to use as constant expressions
constexpr int mf = 20;        // 20 is a constant expression
constexpr int limit = mf + 1; // mf + 1 is a constant expression
constexpr int sz = size();    // ok only if size is a constexpr function

Possible Types

  • The types we can use in a constexpr are known as literal types
    • eg. arithmetic, reference, and pointer types

constexpr Pointer

  • pointers and references as constexprs must be initialized from
    • the nullptr literal
    • the literal (i.e., constant expression) 0
    • an object that remains at a fixed address, eg.
      • an object defined outside of any function (those defined inside are not stored at fixed addresses)
      • local static objects/variables
// the `constexpr` specifier applies to the pointer, not the type to which the pointer points:
const int *p = nullptr;       // p is a pointer to a const int
constexpr int *q = nullptr;   // q is a const pointer to int

// a constexpr pointer may point to a const or a nonconst type:
constexpr int *np = nullptr;  // np is a constant pointer to int that is null
int j = 0;
constexpr int i = 42;         // type of i is const int
// i and j must be defined outside any function
constexpr const int *p = &i;  // p is a constant pointer to the const int i
constexpr int *p1 = &j;       // p1 is a constant pointer to the int j

constexpr Functions

Note: these are the C++11 rules (the C++14 rules are in the next section)

  • the new standard lets us define certain functions as constexpr
  • must be simple enough that the compiler can evaluate them at compile time
  • can be used in the initializer of a constexpr variable
  • defined like any other function but must meet certain restrictions:
    • return type and the type of each parameter must be a literal type
    • function body must contain exactly one return statement
constexpr int new_sz() { return 42; }
constexpr int foo = new_sz(); // ok: foo is a constant expression
  • compiler will replace a call to a constexpr function with its resulting value
  • constexpr functions are implicitly inline
  • function body may contain other statements so long as those statements generate no actions at runtime
    • eg. null statements, type aliases, and using declarations
  • is permitted to return a value that is not a constant:
// scale(cnt) is a constant expression if "cnt" is a constant expression
constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }

int i = 2;          // i is not a constant expression
int arr2[scale(i)]; // error: scale(i) is not a constant expression
int arr[scale(2)];  // ok: scale(2) is a constant expression

Constant Expressions (C++14)

  • with C++14, constexpr functions can make use of most control structures available in general C++ code (eg. for loops)
  • the compiler can evaluate a constexpr function at compile time, but it need not necessarily do so
    • in a context that requires a compile-time value (e.g., an array length or a nontype template argument), the compiler will attempt to evaluate a call to a constexpr function at compile time and issue an error if that is not possible (since a constant must be produced in the end)
    • in other contexts, the compiler may or may not attempt the evaluation at compile time but if such an evaluation fails, no error is issued and the call is left as a run-time call instead
      • at block scope, the compiler can decide whether to compute it at compile or run time
// assume "isPrime" is a constexpr function
constexpr bool b1 = isPrime(9); // evaluated at compile time
const bool b2 = isPrime(9);     // evaluated at compile time if in namespace scope

bool fiftySevenIsPrime() {
  return isPrime(57);           // evaluated at compile or running time
                                // ("at block scope, the compiler can decide whether 
                                // to compute it at compile or run time")
}

int x;
...
std::cout << isPrime(x);        // evaluated at run time

Type Aliases

  • A type alias
    • is a type name
    • can appear wherever a type name can appear

typedef

  • scope: Typedef names are only in effect in the scope where they are visible: different functions or class declarations may define identically-named types with different meaning. (cppreference)
  • do not substitute: do not interpret a declaration that uses a type alias by conceptually “replacing” the alias with its corresponding type
// do not simply substitute this
typedef char *pstring;

const pstring cstr = 0; // - cstr is a constant pointer to char, ie. char *const cstr
                        // - base type is const char* (a pointer type)
const pstring *ps;      // ps is a pointer to a constant pointer to char

// WRONG interpretation:
const char *cstr = 0;   // wrong interpretation of const pstring cstr
// - declares cstr as a pointer to const char rather than as a const pointer to char
// - the base type is char (without asterisk!)
// - the asterisk is part of the declarator!

using

  • C++11
  • aka alias declaration
using SI = Sales_item;      // SI is a synonym for Sales_item

Types

List of Types

cppreference: list

Lippman:

  • primitive types (aka in Lippman: “built-in types”, in cppreference: “fundamental types”)
    • arithmetic types
      • integral types (char, int, bool)
      • floating-point types (float, double)
    • void
  • non-primitive types
    • class types
      • library types (e.g. string, istream, ostream)
    • compound types (2.3 “a type that is defined in terms of another type”)
      • pointer types
      • reference types (2.3.1)
      • array types
      • container types (9)
        • sequential (full list, see table 9.1) (“element order corresponds to the position at which elements are put into the container”)
          • string (see “C Notes”)
          • array
          • vector
          • linked lists
            • list (doubly linked)
            • forward_list (singly linked)
        • associative (“store elements based on the value of a key”)
        • unordered

typeid(a).name()

  • Problem with this approach: see stackoverflow
    • throws away cv-qualifiers (ie. const, volatile), references, and lvalue/rvalue-ness (because the standard mandates this behavior)

Incomplete Types

  • after a declaration and before a definition is seen, a type is an incomplete type

Forward Declarations

// forward declaration:
// declare a class without defining it
class Screen; // declaration of the Screen class
  • We can
    • define pointers or references to such types
    • declare (but not define) functions that use an incomplete type as a parameter or return type
  • we cannot
    • create objects of that type
    • use a reference or pointer to access a member of the type

Examples:

  • data members of the class’ own type:
    • Because a class is not defined until its class body is complete, a class cannot have data members of its own type
    • However, a class is considered declared (but not yet defined) as soon as its class name has been seen. Therefore, a class can have data members that are pointers or references to its own type

Cast / Typecast (explicit conversion)

Lip, p165

  • general rule: avoid casts!
  • reinterpret_casts:
    • Such casts are always hazardous
  • const_cast:
    • can be useful in the context of overloaded functions
    • Other uses of const_cast often indicate a design flaw
  • static_cast and dynamic_cast:
    • should be needed infrequently
  • every time you write a cast, you should think hard about whether you can achieve the same result in a different way
  • if the cast is unavoidable, errors can be mitigated by limiting the scope in which the cast value is used and by documenting all assumptions about the types involved

Named Casts

  • static_cast, dynamic_cast, const_cast, and reinterpret_cast

const_cast

  • changes only a low-level const in its operand
  • “casts away the const
  • using a const_cast in order to write to a const object is undefined

static_cast

  • any well-defined type conversion, other than those involving low-level const, can be requested using a static_cast
  • useful for:
    • assigning a larger arithmetic type to a smaller type
      • static_cast to inform both the reader of the program and the compiler that we are aware of and are not concerned about the potential loss of precision
      • when we do an explicit cast, the compiler warning message is turned off
    • to retrieve a pointer value that was stored in a void* pointer
      • warning: must be certain that the type to which we cast the pointer is the actual type of that pointer; if the types do not match, the result is undefined
void* p = &d; // ok: address of any nonconst object can be stored in a void*
// ok: converts void* back to the original pointer type
// pointer value is preserved: ie. result of the cast will be equal to the original address value "&d"
double *dp = static_cast<double*>(p);

cppreference:

3) If there is an implicit conversion sequence from expression to target-type, or if overload resolution for a direct initialization of an object or reference of type target-type from expression would find at least one viable function, then static_cast<target-type>(expression) returns the imaginary variable Temp initialized as if by target-type Temp(expression);, which may involve implicit conversions, a call to the constructor of target-type or a call to a user-defined conversion operator.

Cast to void

// - https://stackoverflow.com/a/61205643/12282296
//   - see "variadic_templates_pack_expansion.cpp"
//     - note: phth modified the original code
//       - remove phth's changes and "static_cast<void>(temp);" to see the warning
int temp[] = { (s += toString(args),0)... };
static_cast<void>(temp);
  • Casting a variable expression to void to suppress this (“unused local variables or function parameters”) warning has become an idiom in the C and later C++ community, stackoverflow

C-Style Casts

type(expr); // function-style cast notation
(type)expr; // C-language-style cast notation
  • where a static_cast or a const_cast would be legal, the old-style cast does the same conversion as the respective named cast
  • if neither cast is legal, then an old-style cast performs a reinterpret_cast

cppreference:

When the C-style cast expression is encountered, the compiler attempts to interpret it as the following cast expressions, in this order:

  • a) const_cast<target-type>(expression);
  • b) static_cast<target-type>(expression), (…)
  • c) static_cast (with extensions) followed by const_cast;
  • d) reinterpret_cast<target-type>(expression);
  • e) reinterpret_cast followed by const_cast.

The first choice that satisfies the requirements of the respective cast operator is selected, even if it cannot be compiled (see example). If the cast can be interpreted in more than one way as static_cast followed by a const_cast, it cannot be compiled.

Conditional Statements

switch Statement

  • the break and default keywords are optional
    • break breaks out of the switch block
    • default specifies some code to run if there is no case match
  • Fallthrough: “Fall through is a type of error that occurs in various programming languages like C, C++, Java, Dart …etc. It occurs in switch-case statements where when we forget to add a break statement and in that case flow of control jumps to the next line. Due to this, when any case is matched with the specified value, then control falls through to subsequent cases until a break statement is found.”, geeksforgeeks
switch(expression) {
  case x:
    // code block
    break;
  case y:
    // code block
    break;
  default:
    // code block
}

Example

// https://www.w3schools.com/cpp/cpp_switch.asp
int day = 4;
switch (day) {
  case 6:
    cout << "Today is Saturday";
    break;
  case 7:
    cout << "Today is Sunday";
    break;
  default:
    cout << "Looking forward to the Weekend";
}
// Outputs "Looking forward to the Weekend" 

argc, argv

see stackoverflow:

argv and argc are how command line arguments are passed to main() in C and C++.

argc will be the number of strings pointed to by argv. This will (in practice) be 1 plus the number of arguments, as virtually all implementations will prepend the name of the program to the array.

The variables are named argc (argument count) and argv (argument vector) by convention, but they can be given any valid identifier: int main(int num_args, char** arg_strings) is equally valid.

They can also be omitted entirely, yielding int main(), if you do not intend to process command line arguments.

Try the following program:

#include <iostream>

int main(int argc, char** argv) {
    std::cout << "Have " << argc << " arguments:" << std::endl;
    for (int i = 0; i < argc; ++i) {
        std::cout << argv[i] << std::endl;
    }
}

Running it with ./test a1 b2 c3 will output

Have 4 arguments:
./test
a1
b2
c3

Debugging

see “C notes”

tools:

  • gdb
  • valgrind