Modern C++
Programming
4. Basic Concepts III
Entities and Control Flow
Federico Busato
2024-03-29
Table of Contents
1 Entities
2 Declaration and Definition
3 Enumerators
4 struct, Bitfield, and union
struct
Anonymous and Unnamed struct
Bitfield
union
1/38
Table of Contents
5 [[deprecated]] Attribute
6 Control Flow
if Statement
for and while Loops
Range-based for Loop
switch
goto
Avoid Unused Variable Warning
2/38
Entities
Entities
A C++ program is set of language-specific keywords (for, if, new, true, etc.),
identifiers (symbols for variables, functions, structures, namespaces, etc.), expressions
defined as sequence of operators, and literals (constant value tokens)
C++ Entity
An entity is a value, object, reference, function, enumerator, type, class member, or
template
Identifiers and user-defined operators are the names used to refer to entities
Entities also captures the result(s) of an expression
Preprocessor macros are not C++ entities
3/38
Declaration and
Definition
Declaration/Definition
Declaration/Prototype
A declaration (or prototype) introduces an entity with an identifier describing its
type and properties
A declaration is what the compiler and the linker needs to accept references (usage) to
that identifier
Entities can be declared multiple times. All declarations are the same
Definition/Implementation
An entity definition is the implementation of a declaration. It defines the properties
and the behavior of the entity
For each entity, only a single definition is allowed
4/38
Declaration/Definition Function Example
void f(int a, char* b); // function declaration
void f(int a, char*) { // function definition
... // "b" can be omitted if not used
}
void f(int a, char* b); // function declaration
// multiple declarations is valid
f(3, "abc"); // usage
void g(); // function declaration
g(); // linking error "g" is not defined
5/38
Declaration/Definition struct Example
A declaration without a concrete implementation is an incomplete type (as void )
struct A; // declaration 1
struct A; // declaration 2 (ok)
struct B { // declaration and definition
int b;
// A x; // compile error incomplete type
A* y; // ok, pointer to incomplete type
};
struct A { // definition
char c;
}
6/38
Enumerators
Enumerator - enum
Enumerator
An enumerator enum is a data type that groups a set of named integral constants
enum color_t { BLACK, BLUE, GREEN };
color_t color = BLUE;
cout << (color == BLACK); // print false
The problem:
enum color_t { BLACK, BLUE, GREEN };
enum fruit_t { APPLE, CHERRY };
color_t color = BLACK; // int: 0
fruit_t fruit = APPLE; // int: 0
bool b = (color == fruit); // print 'true'!!
// and, most importantly, does the match between a color and
// a fruit make any sense?
7/38
Strongly Typed Enumerator - enum class
enum class (C++11)
enum class (scoped enum) data type is a type safe enumerator that is not implicitly
convertible to int
enum class Color { BLACK, BLUE, GREEN };
enum class Fruit { APPLE, CHERRY };
Color color = Color::BLUE;
Fruit fruit = Fruit::APPLE;
// bool b = (color == fruit) compile error we are trying to match colors with fruits
// BUT, they are different things entirely
// int a1 = Color::GREEN; compile error
// int a2 = Color::RED + Color::GREEN; compile error
int a3 = (int) Color::GREEN; // ok, explicit conversion
8/38
enum/enum class Features
enum/enum class can be compared
enum class Color { RED, GREEN, BLUE };
cout << (Color::RED < Color::GREEN); // print true
enum/enum class are automatically enumerated in increasing order
enum class Color { RED, GREEN = -1, BLUE, BLACK };
// (0) (-1) (0) (1)
Color::RED == Color::BLUE; // true
enum/enum class can contain alias
enum class Device { PC = 0, COMPUTER = 0, PRINTER };
C++11 enum/enum class allows setting the underlying type
enum class Color : int8_t { RED, GREEN, BLUE };
9/38
enum class Features - C++17
C++17 enum class supports direct-list-initialization
enum class Color { RED, GREEN, BLUE };
Color a{2}; // ok, equal to Color:BLUE
C++17 enum/enum class support attributes
enum class Color { RED, GREEN, BLUE [[deprecated]] };
auto x = Color::BLUE; // compiler warning
10/38
enum class Features - C++20
C++20 allows introducing the enumerator identifiers into the local scope to
decrease the verbosity
enum class Color { RED, GREEN, BLUE };
switch (x) {
using enum Color; // C++20
case RED:
case GREEN:
case BLUE:
}
11/38
enum/enum class - Common Errors
enum/enum class should be always initialized
enum class Color { RED, GREEN, BLUE };
Color my_color; // "my_color" may be outside RED, GREEN, BLUE!!
C++17 Cast from out-of-range values respect to the underlying type of
enum/enum class leads to undefined behavior
enum Color : uint8_t { RED, GREEN, BLUE };
Color value = 256; // undefined behavior
12/38
enum/enum class and constexpr
C++17 constexpr expressions don’t allow out-of-range values for (only) enum
without explicit underlying type
enum Color { RED };
enum Fruit : int { APPLE };
enum class Device { PC };
// constexpr Color a1 = (Color) -1; compile error
const Color a2 = (Color) -1; // ok
constexpr Fruit a3 = (Fruit) -1; // ok
constexpr Device a4 = (Device) -1; // ok
Construction Rules for enum class Values
13/38
struct, Bitfield, and
union
struct 1/2
A struct (structure) aggregates different variables into a single unit
struct A {
int x;
char y;
};
It is possible to declare one or more variables after the definition of a struct
struct A {
int x;
} a, b;
Enumerators can be declared within a struct without a name
struct A {
enum {X, Y}
};
A::X;
14/38
struct 2/2
It is possible to declare a struct in a local scope (with some restrictions), e.g.
function scope
int f() {
struct A {
int x;
} a;
return a.x;
}
15/38
Anonymous and Unnamed struct
Unnamed struct : a structured without a name, but with an associated type
Anonymous struct : a structured without a name and type
The C++ standard allows unnamed struct but, contrary to C, does not allow
anonymous struct (i.e. without a name)
struct {
int x;
} my_struct; // unnamed struct, ok
struct S {
int x;
struct { int y; }; // anonymous struct, compiler warning with -Wpedantic
}; // -Wpedantic: diagnose use of non-strict ISO C++ extensions
16/38
Bitfield
Bitfield
A bitfield is a variable of a structure with a predefined bit width. A bitfield can hold
bits instead bytes
struct S1 {
int b1 : 10; // range [0, 1023]
int b2 : 10; // range [0, 1023]
int b3 : 8; // range [0, 255]
}; // sizeof(S1): 4 bytes
struct S2 {
int b1 : 10;
int : 0; // reset: force the next field
int b2 : 10; // to start at bit 32
}; // sizeof(S1): 8 bytes
Useful to compress different values that have specific ranges (e.g. S1)
”Free” modulo operation 2
x
(e.g. S1.b1 modulo 2
10
)
17/38
union 1/2
Union
A union is a special data type that allows to store different data types in the same
memory location
The union is only as big as necessary to hold its largest data member
The union is a kind of “overlapping” storage
18/38
union 2/2
union A {
int x;
char y;
}; // sizeof(A): 4
A a;
a.x = 1023; // bits: 00..000001111111111
a.y = 0; // bits: 00..000001100000000
cout << a.x; // print 512 + 256 = 768
NOTE: Little-Endian encoding maps the bytes of a value in memory in the reverse order. y
maps to the last byte of x
Contrary to struct , C++ allows anonymous union (i.e. without a name)
C++17 introduces std::variant to represent a type-safe union
19/38
[[deprecated]]
Attribute
[[deprecated]] Attribute 1/3
C++14 allows to deprecate, namely discourage, use of entities by adding the
[[deprecated]] attribute, optionally with a message
[[deprecated("reason")]] . It applies to:
Functions
Variables
Classes and structures
Enumerators. Single value enumerator in C++17
Types
Namespaces
20/38
[[deprecated]] Attribute 2/3
[[deprecated]] void f() {}
struct [[deprecated]] S1 {};
using MyInt [[deprecated]] = int;
struct S2 {
[[deprecated]] int var = 3;
[[deprecated]] static constexpr int var2 = 4;
};
f(); // warning
S1 s1; // warning
MyInt i; // warning
S2{}.var; // warning
21/38
[[deprecated]] Attribute 3/3
enum [[deprecated]] E { EnumValue };
enum class MyEnum { A, B [[deprecated]] = 42 }; // C++17
namespace [[deprecated("please use my_ns_v2")]] my_ns {
const int x = 5;
}
auto x = EnumValue; // warning
MyEnum::B; // warning
my_ns::x; // warning, "please use my_ns_v2"
22/38
Control Flow
if Statement
The if statement executes the first branch if the specified condition is evaluated to
true , the second branch otherwise
Short-circuiting:
if (<true expression> r| array[-1] == 0)
... // no error!! even though index is -1
// left-to-right evaluation
Ternary operator:
<cond> ? <expression1> : <expression2>
<expression1> and <expression2> must return a value of the same or convertible
type
int value = (a == b) ? a : (b == c ? b : 3); // nested
23/38
for and while Loops
for
for ([init]; [cond]; [increment]) {
...
}
To use when number of iterations is known
while
while (cond) {
...
}
To use when number of iterations is not known
do while
do {
...
} while (cond);
To use when number of iterations is not known, but there is at least one iteration
24/38
for Loop Features and Jump Statements
C++ allows “in loop” definitions:
for (int i = 0, k = 0; i < 10; i++, k += 2)
...
Infinite loop:
for (;;) // also while(true);
...
Jump statements (break, continue, return):
for (int i = 0; i < 10; i++) {
if (<condition>)
break; // exit from the loop
if (<condition>)
continue; // continue with a new iteration and exec. i++
return; // exit from the function
}
25/38
Range-based for Loop 1/3
C++11 introduces the range-based for loop to simplify the verbosity of traditional
for loop constructs. They are equivalent to the for loop operating over a range of
values, but safer
The range-based for loop avoids the user to specify start, end, and increment of the
loop
for (int v : { 3, 2, 1 }) // INITIALIZER LIST
cout << v << " "; // print: 3 2 1
int values[] = { 3, 2, 1 };
for (int v : values) // ARRAY OF VALUES
cout << v << " "; // print: 3 2 1
for (auto c : "abcd") // RAW STRING
cout << c << " "; // print: a b c d
26/38
Range-based for Loop 2/3
Range-based for loop can be applied in three cases:
Fixed-size array int array[3] , "abcd"
Branch Initializer List {1, 2, 3}
Any object with begin() and end() methods
std::vector vec{1, 2, 3, 4};
for (auto x : vec) {
cout << x << ", ";
// print: "1, 2, 3, 4"
int matrix[2][4];
for (auto& row : matrix) {
for (auto element : row)
cout << "@";
cout << "\n";
}
// print: @@@@
// @@@@
27/38
Range-based for Loop 3/3
C++17 extends the concept of range-based loop for structure binding
struct A {
int x;
int y;
};
A array[] = { {1,2}, {5,6}, {7,1} };
for (auto [x1, y1] : array)
cout << x1 << "," << y1 << " "; // print: 1,2 5,6 7,1
28/38
switch 1/2
The switch statement evaluates an expression ( int , char , enum class , enum )
and executes the statement associated with the matching case value
char x = ...
switch (x) {
case 'a': y = 1; break;
default: return -1;
}
return y;
Switch scope:
int x = 1;
switch (1) {
case 0: int x; // nearest scope
case 1: cout << x; // undefined!!
case 2: { int y; } // ok
// case 3: cout << y; // compile error
}
29/38
switch 2/2
Fall-through:
MyEnum x
int y = 0;
switch (x) {
case MyEnum::A: // fall-through
case MyEnum::B: // fall-through
case MyEnum::C: return 0;
default: return -1;
}
C++17 [[fallthrough]] attribute
char x = ...
switch (x) {
case 'a': x++;
[[fallthrough]]; // C++17: avoid warning
case 'b': return 0;
default: return -1;
}
30/38
Control Flow with Initializing Statement
Control flow with initializing statement aims at simplifying complex actions before
the condition evaluation and restrict the scope of a variable which is visible only in the
control flow body
C++17 introduces if statement with initializer
if (int ret = x + y; ret < 10)
cout << ret;
C++17 introduces switch statement with initializer
switch (auto i = f(); x) {
case 1: return i + x;
C++20 introduces range-for loop statement with initializer
for (int i = 0; auto x : {'A', 'B', 'C'})
cout << i++ << ":" << x << " "; // print: 0:A 1:B 2:C
31/38
goto 1/4
When goto could be useful:
bool flag = true;
for (int i = 0; i < N && flag; i++) {
for (int j = 0; j < M && flag; j++) {
if (<condition>)
flag = false;
}
}
become:
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (<condition>)
goto LABEL;
}
}
LABEL: ;
32/38
goto 2/4
Best solution:
bool my_function(int M, int M) {
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (<condition>)
return false;
}
}
return true;
}
33/38
goto 3/4
34/38
goto 4/4
35/38
Avoid Unused Variable Warning 1/3
Most compilers issue a warning when a variable is unused. There are different
situations where a variable is expected to be unused
// EXAMPLE 1: macro dependency
int f(int value) {
int x = value;
# if defined(ENABLE_SQUARE_PATH)
return x * x;
# else
return 0;
# endif
}
36/38
Avoid Unused Variable Warning
2/3
// EXAMPLE 2: constexpr dependency (MSVC)
template<typename T>
int f(T value) {
if constexpr (sizeof(value) >= 4)
return 1;
else
return 2;
}
// EXAMPLE 3: decltype dependency (MSVC)
template<typename T>
int g(T value) {
using R = decltype(value);
return R{};
}
37/38
Avoid Unused Variable Warning 3/3
There are different ways to solve the problem depending on the standard used
Before C++17: static cast<void>(var)
C++17 [[maybe unused]] attribute
C++26 auto
[[maybe unused]] int x = value;
int y = 3;
static_cast<void>(y);
auto _ = 3;
auto _ = 4; // _ repetition is not an error
void f([[maybe unused]] int x) {}
38/38