Modern C++
Programming
2. Basic Concepts I
Type System, Fundamental Types, and Operators
Federico Busato
2024-03-29
Table of Contents
1 The C++ Type System
Type Categories
Type Properties ⋆
2 Fundamental Types Overview
Arithmetic Types
Suffix and Prefix
Non-Standard Arithmetic Types
void Type
nullptr
1/29
Table of Contents
3 Conversion Rules
4 auto Keyword
5 C++ Operators
Operators Precedence
Prefix/Postfix Increment/Decrement Semantic
Assignment, Compound, and Comma Operators
Spaceship Operator <=> ⋆
Safe Comparison Operators ⋆
2/29
The C++ Type
System
The C++ Type System
C++ is a strongly typed and statically typed language
Every entity has a type and that type never changes
Every variable, function, or expression has a type in order to be compiled. Users can
introduce new types with class or struct
The type specifies:
• The amount of memory allocated for the variable (or expression result)
• The kinds of values that may be stored and how the compiler interprets the bit
patterns in those values
• The operations that are permitted for those entities and provides semantics
3/29
Type Categories
C++ organizes the language types in two main categories:
• Fundamental types: Types provided by the language itself
• Arithmetic types: integer and floating point
• void
• nullptr C++11
• Compound types: Composition or references to other types
• Pointers
• References
• Enumerators
• Arrays
• struct , class , union
• Functions
4/29
⋆
Type Properties 1/2
C++ types can be also classified based on their properties:
• Objects:
• size: sizeof is defined
• alignment requirement: alignof is defined
• storage duration: describe when an object is allocated and deallocated
• lifetime, bounded by storage duration or temporary
• value, potentially indeterminate
• optionally, a name.
Types: Arithmetic, Pointers and nullptr , Enumerators, Arrays, struct ,
class , union
5/29
⋆
Type Properties 2/2
• Scalar:
• Hold a single value and is not composed of other objects
• Trivially Copyable: can be copied bit for bit
• Standard Layout: compatible with C functions and structs
• Implicit Lifetime: no user-provided constructor or destructor
Types: Arithmetic, Pointers and nullptr , Enumerators
• Trivial types: Trivial default/copy constructor, copy assignment operator, and
destructor → Trivially Copyable
Types: Scalar, trivial class types, arrays of such types
• Incomplete types: A type that has been declared but not yet defined
Types: void , incompletely-defined object types, e.g. struct A; , array of elements of
incomplete type
6/29
C++ Types Summary
7/29
Fundamental Types
Overview
Arithmetic Types - Integral
Fixed width types
Native Type Bytes Range
<cstdint>
bool 1 true, false
char † 1 implementation defined
signed char 1 -128 to 127 int8 t
unsigned char 1 0 to 255 uint8 t
short 2 -215 to 215 -1 int16 t
unsigned short 2 0 to 216 -1 uint16 t
int 4 -231 to 231 -1 int32 t
unsigned int 4 0 to 232 -1 uint32 t
long int 4/8∗ int32 t/int64 t
long unsigned int 4/8∗ uint32 t/uint64 t
long long int 8 -263 to 263 -1 int64 t
long long unsigned int 8 0 to 264 -1 uint64 t
∗ † 8/29
4 bytes on Windows64 systems, signed/unsigned, two-complement from C++11
Arithmetic Types - Floating-Point
Fixed width types
Native Type IEEE Bytes Range
C++23 <stdfloat>
(bfloat16) N 2 ±1.18 × 10−38 to ±3.4 × 10+38 std::bfloat16 t
(float16) Y 2 0.00006 to 65, 536 std::float16 t
float Y 4 ±1.18 × 10−38 to ±3.4 × 10+38 std::float32 t
double Y 8 ±2.23 × 10−308 to ±1.8 × 10+308 std::float64 t
9/29
Arithmetic Types - Short Name
Signed Type short name
signed char /
signed short int short
signed int int
signed long int long
signed long long int long long
Unsigned Type short name
unsigned char /
unsigned short int unsigned short
unsigned int unsigned
unsigned long int unsigned long
unsigned long long int unsigned long long 10/29
Arithmetic Types - Suffix (Literals)
Type SUFFIX Example Notes
int / 2
unsigned int u, U 3u
long int l, L 8L
long unsigned ul, UL 2ul
long long int ll, LL 4ll
long long unsigned int ull, ULL 7ULL
float f, F 3.0f only decimal numbers
double 3.0 only decimal numbers
C++23 Type SUFFIX Example Notes
std::bfloat16 t bf16, BF16 3.0bf16 only decimal numbers
std::float16 t f16, F16 3.0f16 only decimal numbers
std::float32 t f32, F32 3.0f32 only decimal numbers
std::float64 t f64, F64 3.0f64 only decimal numbers
std::float128 t f128, F128 3.0f128 only decimal numbers
11/29
Arithmetic Types - Prefix (Literals)
Representation PREFIX Example
Binary C++14 0b 0b010101
Octal 0 0307
Hexadecimal 0x or 0X 0xFFA010
C++14 also allows digit separators for improving the readability 1'000'000
12/29
Other Arithmetic Types
• C++ also provides long double (no IEEE-754) of size 8/12/16 bytes
depending on the implementation
• Reduced precision floating-point supports before C++23:
- Some compilers provide support for half (16-bit floating-point) (GCC for ARM: fp16 ,
LLVM compiler: half )
- Some modern CPUs and GPUs provide half instructions
- Software support: OpenGL, Photoshop, Lightroom, half.sourceforge.net
• C++ does not provide 128-bit integers even if some architectures support it.
clang and gcc allow 128-bit integers as compiler extension ( int128 )
13/29
void Type
void is an incomplete type (not defined) without a value
• void indicates also a function with no return type or no parameters
e.g. void f() , f(void)
• In C sizeof(void) == 1 (GCC), while in C++ sizeof(void) does not
compile!!
int main() {
// sizeof(void); // compile error
}
14/29
nullptr Keyword
C++11 introduces the keyword nullptr to represent a null pointer ( 0x0 ) and
replacing the NULL macro
nullptr is an object of type nullptr t → safer
int* p1 = NULL; // ok, equal to int* p1 = 0l
int* p2 = nullptr; // ok, nullptr is convertible to a pointer
int n1 = NULL; // ok, we are assigning 0 to n1
//int n2 = nullptr; // compile error nullptr is not convertible to an integer
//int* p2 = true ? 0 : nullptr; // compile error incompatible types
15/29
Conversion Rules
Conversion Rules
Implicit type conversion rules, applied in order, before any operation:
⊗: any operation (*, +, /, -, %, etc.)
(A) Floating point promotion
floating type ⊗ integer type → floating type
(B) Implicit integer promotion
small integral type := any signed/unsigned integral type smaller than int
small integral type ⊗ small integral type → int
(C) Size promotion
small type ⊗ large type → large type
(D) Sign promotion
signed type ⊗ unsigned type → unsigned type 16/29
Examples and Common Errors
float f = 1.0f;
unsigned u = 2;
int i = 3;
short s = 4;
uint8_t c = 5; // unsigned char
f * u; // float × unsigned → float: 2.0f
s * c; // short × unsigned char → int: 20
u * i; // unsigned × int → unsigned: 6u
+c; // unsigned char → int: 5
Integers are not floating points!
int b = 7;
float a = b / 2; // a = 3 not 3.5!!
int c = b / 2.0; // again c = 3 not 3.5!!
17/29
Implicit Promotion
Integral data types smaller than 32-bit are implicitly promoted to int , independently
if they are signed or unsigned
• Unary +, -, ∼ and Binary +, -, &, etc. promotion:
char a = 48; // '0'
cout << a; // print '0'
cout << +a; // print '48'
cout << (a + 0); // print '48'
uint8_t a1 = 255;
uint8_t b1 = 255;
cout << (a1 + b1); // print '510' (no overflow)
18/29
auto Keyword
auto Keyword 1/3
C++11 The auto keyword specifies that the type of the variable will be automatically
deduced by the compiler (from its initializer)
auto a = 1 + 2; // 1 is int, 2 is int, 1 + 2 is int!
// -> 'a' is "int"
auto b = 1 + 2.0; // 1 is int, 2.0 is double. 1 + 2.0 is double
// -> 'b' is "double"
auto can be very useful for maintainability and for hiding complex type definitions
for (auto i = k; i < size; i++)
...
On the other hand, it may make the code less readable if excessively used because of
type hiding
Example: auto x = 0; in general makes no sense ( x is int )
19/29
⋆
auto Keyword - Functions 2/3
In C++11/C++14, auto (as well as decltype ) can be used to define function
output types
auto g(int x) -> int { return x * 2; } // C++11
// "-> int" is the deduction type
// a better way to express it is:
auto g2(int x) -> decltype(x * 2) { return x * 2; } // C++11
auto h(int x) { return x * 2; } // C++14
//--------------------------------------------------------------
int x = g(3); // C++11
20/29
⋆
auto Keyword - Functions 3/3
In C++20, auto can be also used to define function input
void f(auto x) {}
// equivalent to templates but less expensive at compile-time
//--------------------------------------------------------------
f(3); // 'x' is int
f(3.0); // 'x' is double
21/29
C++ Operators
Operators Overview
Precedence Operator Description Associativity
1 a++ a-- Suffix/postfix increment and decrement Left-to-right
+a -a ++a --a Plus/minus, Prefix increment/decrement,
2 Right-to-left
! not ∼ Logical/Bitwise Not
3 a*b a/b a%b Multiplication, division, and remainder Left-to-right
4 a+b a-b Addition and subtraction Left-to-right
5 ≪ ≫ Bitwise left shift and right shift Left-to-right
6 < <= > >= Relational operators Left-to-right
7 == != Equality operators Left-to-right
8 & Bitwise AND Left-to-right
9 ˆ Bitwise XOR Left-to-right
10 | Bitwise OR Left-to-right
11 && and Logical AND Left-to-right
12 || or Logical OR Left-to-right
+= -= *= /= %=
13 Compound Right-to-left
<<= >>= &= ˆ= |= 22/29
Operators Precedence 1/2
• Unary operators have higher precedence than binary operators
• Standard math operators (+, *, etc.) have higher precedence than
comparison, bitwise, and logic operators
• Bitwise and logic operators have higher precedence than comparison operators
• Bitwise operators have higher precedence than logic operators
• Compound assignment operators += , -= , *= , /= , %= , ˆ= , != , &= ,
>>= , <<= have lower priority
• The comma operator has the lowest precedence (see next slides)
23/29
en.cppreference.com/w/cpp/language/operator precedence
Operators Precedence 2/2
Examples:
a + b * 4; // a + (b * 4)
a * b / c % d; // ((a * b) / c) % d
a + b < 3 >> 4; // (a + b) < (3 >> 4)
a && b && c || d; // (a && b && c) || d
a and b and c or d; // (a && b && c) || d
a | b & c || e && d; // ((a | (b & c)) || (e && d)
Important: sometimes parenthesis can make an expression verbose... but they can
help! 24/29
Prefix/Postfix Increment Semantic
Prefix Increment/Decrement ++i , --i
(1) Update the value
(2) Return the new (updated) value
Postfix Increment/Decrement i++ , i--
(1) Save the old value (temporary)
(2) Update the value
(3) Return the old (original) value
Prefix/Postfix increment/decrement semantic applies not only to built-in types but
also to objects
25/29
⋆
Operation Ordering Undefined Behavior
Expressions with undefined (implementation-defined) behavior:
int i = 0;
i = ++i + 2; // until C++11: undefined behavior
// since C++11: i = 3
i = 0;
i = i++ + 2; // until C++17: undefined behavior
// since C++17: i = 3
f(i = 2, i = 1); // until C++17: undefined behavior
// since C++17: i = 2
i = 0;
a[i] = ++i; // until C++17: undefined behavior
// since C++17: a[1] = 1
f(++i, ++i); // undefined behavior
i = ++i + i++; // undefined behavior 26/29
Assignment, Compound, and Comma Operators
Assignment and compound assignment operators have right-to-left associativity
and their expressions return the assigned value
int y = 2;
int x = y = 3; // y=3, then x=3
// the same of x = (y = 3)
if (x = 4) // assign x=4 and evaluate to true
The comma operator⋆ has left-to-right associativity. It evaluates the left expression,
discards its result, and returns the right expression
int a = 5, b = 7;
int x = (3, 4); // discards 3, then x=4
int y = 0;
int z;
z = y, x; // z=y (0), then returns x (4) 27/29
⋆
Spaceship Operator <=>
C++20 provides the three-way comparison operator <=> , also called spaceship
operator, which allows comparing two objects similarly of strcmp . The operator
returns an object that can be directly compared with a positive, 0, or negative integer
value
(3 <=> 5) == 0; // false
('a' <=> 'a') == 0; // true
(3 <=> 5) < 0; // true
(7 <=> 5) < 0; // false
The semantic of the spaceship operator can be extended to any object (see next
lectures) and can greatly simplify the comparison operators overloading
28/29
⋆
Safe Comparison Operators
C++20 introduces a set of functions <utility> to safely compare integers of
different types (signed, unsigned)
bool cmp_equal(T1 a, T2 b)
bool cmp_not_equal(T1 a, T2 b)
bool cmp_less(T1 a, T2 b)
bool cmp_greater(T1 a, T2 b)
bool cmp_less_equal(T1 a, T2 b)
bool cmp_greater_equal(T1 a, T2 b)
example:
# include <utility>
unsigned a = 4;
int b = -3;
bool v1 = (a > b); // false!!!, see next slides
bool v2 = std::cmp_greater(a, b); // true
29/29
How to compare signed and unsigned integers in C++20?