Lecture Slides For Programming in c++-2018-02-15 PDF
Lecture Slides For Programming in c++-2018-02-15 PDF
Michael D. Adams
For additional information and resources related to these lecture slides (including errata and lecture
videos covering the material on many of these slides), please visit:
http://www.ece.uvic.ca/˜mdadams/cppbook
If you like these lecture slides, please show your support by posting a review of them on Google Play:
https://play.google.com/store/search?q=Michael%20D%20Adams%20C%2B%2B&c=books
The author has taken care in the preparation of this document, but makes no expressed or implied warranty of any kind and assumes no
responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use
of the information or programs contained herein.
Copyright
c 2015, 2016, 2017, 2018 Michael D. Adams
Published by the University of Victoria, Victoria, British Columbia, Canada
This document is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported (CC BY-NC-ND 3.0) License. A copy
of this license can be found on page iii of this document. For a simple explanation of the rights granted by this license, see:
http://creativecommons.org/licenses/by-nc-nd/3.0/
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 iii
License II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 iv
License III
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 v
License IV
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 vi
License V
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 vii
License VI
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 viii
License VII
required to be, granted under the terms of this License), and this
License will continue in full force and effect unless terminated as
stated above.
8. Miscellaneous
a. Each time You Distribute or Publicly Perform the Work or a Collection,
the Licensor offers to the recipient a license to the Work on the same
terms and conditions as the license granted to You under this License.
b. If any provision of this License is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this License, and without further action
by the parties to this agreement, such provision shall be reformed to
the minimum extent necessary to make such provision valid and
enforceable.
c. No term or provision of this License shall be deemed waived and no
breach consented to unless such waiver or consent shall be in writing
and signed by the party to be charged with such waiver or consent.
d. This License constitutes the entire agreement between the parties with
respect to the Work licensed here. There are no understandings,
agreements or representations with respect to the Work not specified
here. Licensor shall not be bound by any additional provisions that
may appear in any communication from You. This License may not be
modified without the mutual written agreement of the Licensor and You.
e. The rights granted under, and the subject matter referenced, in this
License were drafted utilizing the terminology of the Berne Convention
for the Protection of Literary and Artistic Works (as amended on
September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
and the Universal Copyright Convention (as revised on July 24, 1971).
These rights and subject matter take effect in the relevant
jurisdiction in which the License terms are sought to be enforced
according to the corresponding provisions of the implementation of
those treaty provisions in the applicable national law. If the
standard suite of rights granted under applicable copyright law
includes additional rights not granted under this License, such
additional rights are deemed to be included in the License; this
License is not intended to restrict the license of any rights under
applicable law.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 ix
License VIII
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 x
Other Textbooks and Lecture Slides by the Author I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 xi
Other Textbooks and Lecture Slides by the Author II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 xii
Other Textbooks and Lecture Slides by the Author III
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 xiii
Part 0
Preface
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 xiv
About These Lecture Slides
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 xv
Acknowledgments
The author would like to thank Robert Leahy for reviewing various drafts of
many of these slides and providing many useful comments that allowed
the quality of these materials to be improved significantly.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 xvi
Disclaimer
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 xvii
Typesetting Conventions
In a definition, the term being defined is often typeset in a font like this.
To emphasize particular words, the words are typeset in a font like this.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 xviii
Part 1
Software
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1
Why Is Software Important?
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2
Why Software-Based Solutions?
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 3
Software-Related Jobs
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 4
Which Language to Learn?
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 5
C
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 6
C++
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 7
Java
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 8
MATLAB
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 9
Fortran
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 10
C#
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 11
Objective C
developed by Tom Love and Brad Cox of Stepstone (later bought by NeXT
and subsequently Apple)
used primarily on Apple Mac OS X and iOS
strict superset of C
no official standard that describes Objective C
authoritative manual on Objective-C 2.0 available from Apple
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 12
Why Learn C++?
vendor neutral
international standard
general purpose
powerful yet efficient
loosely speaking, includes C as subset; so can learn two languages (C++
and C) for price of one
easy to move from C++ to other languages but often not in other direction
many other popular languages inspired by C++
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 13
Part 2
C++
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 14
Section 2.1
History of C++
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 15
Motivation
developed by Bjarne Stroustrup starting in 1979 at Computing Science
Research Center of Bell Laboratories, Murray Hill, NJ, USA
doctoral work in Computing Laboratory of University of Cambridge,
Cambridge, UK
study alternatives for organization of system software for distributed
systems
required development of relatively large and detailed simulator
dissertation:
B. Stroustrup. Communication and Control in Distributed Computer
Systems.
PhD thesis, University of Cambridge, Cambridge, UK, 1979.
in 1979, joined Bell Laboratories after having finished doctorate
work started with attempt to analyze UNIX kernel to determine to what
extent it could be distributed over network of computers connected by LAN
needed way to model module structure of system and pattern of
communication between modules
no suitable tools available
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 16
Objectives
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 18
Timeline for C with Classes (1979–1983) II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 19
Timeline for C with Classes (1979–1983) III
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 20
Timeline for C84 to C++98 (1982–1998) I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 23
Timeline for C84 to C++98 (1982–1998) IV
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 24
Timeline for C84 to C++98 (1982–1998) V
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 25
Timeline for C84 to C++98 (1982–1998) VI
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 26
Timeline for C84 to C++98 (1982–1998) VII
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 27
Timeline for C84 to C++98 (1982–1998) VIII
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 28
Timeline for C84 to C++98 (1982–1998) IX
Jun 1991 second edition of C++PL published
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 29
Timeline for C84 to C++98 (1982–1998) X
May 1992 first IBM C++ release (including templates and exceptions)
Mar 1993 RTTI accepted (Portland, OR, USA)
Jul 1993 namespaces accepted (Munich, Germany)
1993 further work on Cfront Release 4.0 abandoned after failed
attempt to add exception support
Aug 1994 ANSI/ISO Committee Draft registered
Aug 1994 Standard Template Library (STL) accepted (Waterloo, ON, CA);
described in
A. Stepanov and M. Lee. The standard template library.
Technical Report HPL-94-34 (R.1), HP Labs, Aug. 1994.
Aug 1996 export accepted (Stockholm, Sweden)
1997 third edition of C++PL published
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 31
Timeline After C++98 (1998–Present) I
Apr 2001 motion passed to request new work item: technical report on
libraries (Copenhagen, Denmark); later to become ISO/IEC TR
19768:2007
Oct 2003 ISO/IEC 14882:2003 (informally known as C++03) published;
essentially bug fix release; no changes to language from
programmer’s point of view
ISO/IEC 14882:2003 — programming languages — C++,
Oct. 2003.
2003 work on C++0x (now known as C++11) starts
Oct 2004 estimated number of C++ users 3,270,000
Apr 2005 first votes on features for C++0x (Lillehammer, Norway)
2005 auto, static_assert, and rvalue references accepted in
principle
Apr 2006 first full committee (official) votes on features for C++0x (Berlin,
Germany)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 32
Timeline After C++98 (1998–Present) II
Sep 2006 performance technical report (TR 18015) published:
ISO/IEC TR 18015:2006 — information technology —
programming languages, their environments and system
software interfaces — technical report on C++
performance, Sept. 2006.
work spurred by earlier proposal to standardize subset of C++
for embedded systems called Embedded C++ (or just EC++);
EC++ motivated by performance concerns
Apr 2006 decision to move special mathematical functions to separate ISO
standard (Berlin, Germany); deemed too specialized for most
programmers
Nov 2007 ISO/IEC TR 19768:2007 (informally known as C++TR1)
published;
ISO/IEC TR 19768:2007 — information technology —
programming languages — technical report on C++ library
extensions, Nov. 2007.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 33
Timeline After C++98 (1998–Present) III
specifies series of library extensions to be considered for
adoption later in C++
2009 another particularly notable book on C++ published
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 35
Additional Comments
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 36
C++ User Population
Time Estimated Number of Users
Oct 1979 1
Oct 1980 16
Oct 1981 38
Oct 1982 85
Oct 1983 ??+2 (no Cpre count)
Oct 1984 ??+50 (no Cpre count)
Oct 1985 500
Oct 1986 2,000
Oct 1987 4,000
Oct 1988 15,000
Oct 1989 50,000
Oct 1990 150,000
Oct 1991 400,000
Oct 2004 over 3,270,000
above numbers are conservative
1979 to 1991: C++ user population doubled approximately every 7.5
months
stable growth thereafter
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 37
Success of C++
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 40
Evolution of C++
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 41
Standards Documents I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 42
Standards Documents II
ISO/IEC TS 19570:2015 — programming languages — technical
specification for C++ extensions for parallelism, July 2015.
ISO/IEC TS 19841:2015 — technical specification for C++ extensions for
transactional memory, Oct. 2015.
ISO/IEC TS 19217:2015 — programming languages — C++ extensions
for concepts, Nov. 2015.
ISO/IEC TS 19571:2016 — programming languages — technical
specification for C++ extensions for concurrency, Feb. 2016.
ISO/IEC TS 19568:2017 — programming languages — C++ extensions
for library fundamentals, Mar. 2017.
ISO/IEC TS 21425:2017 — programming languages — C++ extensions
for ranges, Nov. 2017.
ISO/IEC 14882:2017 — information technology — programming
languages — C++, Dec. 2017.
ISO JTC1/SC22/WG21 web site. http://www.open-std.org/jtc1/
sc22/wg21/.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 43
Section 2.2
Getting Started
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 44
hello Program: hello.cpp
1 #include <iostream>
2
3 int main()
4 {
5 std::cout << "Hello, world!\n";
6 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 45
Software Build Process
Source Code Compile Link
Object File Executable
File
(.o) Program
(.cpp, .hpp)
.. .. .. ..
. . . .
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 47
Common g++ Command-Line Options
-c
compile only (i.e., do not link)
-o file
use file file for output
-g
include debugging information
-On
set optimization level to n (0 almost none; 3 full)
-std=c++17
conform to C++17 standard
-Idir
specify additional directory dir to search for include files
-Ldir
specify additional directory dir to search for libraries
-llib
link with library lib
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 48
Common g++ Command-Line Options (Continued 1)
-pthread
enable concurrency support (via pthreads library)
-pedantic-errors
strictly enforce compliance with standard
-Wall
enable most warning messages
-Wextra
enable some extra warning messages not enabled by -Wall
-Wpedantic
warn about deviations from strict standard compliance
-Werror
treat all warnings as errors
-fno-elide-constructors
in contexts where standard allows (but does not require) optimization that
omits creation of temporary, do not attempt to perform this optimization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 49
Common g++ Command-Line Options (Continued 2)
-fconstexpr-loop-limit=n
set maximum number of iterations for loop in constexpr functions to n
-fconstexpr-depth=n
set maximum nested evaluation depth for constexpr functions to n
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 50
Clang C++ Compiler
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 51
Common clang++ Command-Line Options
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 52
Manually Building hello Program
g++ -c hello.cpp
2 link object file hello.o to produce executable program hello:
g++ -o hello hello.o
generally, manual building of program is quite tedious, especially when
program consists of multiple source files and additional compiler options
need to be specified
in practice, we use tools to automate build process (e.g., CMake and
Make)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 53
Section 2.3
C++ Basics
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 54
The C++ Programming Language
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 56
Identifiers
identifiers used to name entities such as: types, objects (i.e., variables),
and functions
valid identifier is sequence of one or more letters, digits, and underscore
characters that does not begin with a digit
identifiers that begin with underscore (in many cases) or contain double
underscores are reserved for use by C++ implementation and should be
avoided
examples of valid identifiers:
event_counter
eventCounter
sqrt_2
f_o_o_b_a_r_4_2
identifiers are case sensitive (e.g., counter and cOuNtEr are distinct
identifiers)
identifiers cannot be any of reserved keywords (see next slide)
scope of identifier is context in which identifier is valid (e.g., block,
function, global)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 57
Reserved Keywords
alignas default noexcept this
alignof delete not thread_local
and do not_eq throw
and_eq double nullptr true
asm dynamic_cast operator try
auto else or typedef
bitand enum or_eq typeid
bitor explicit private typename
bool export protected union
break extern public unsigned
case false register using
catch float reinterpret_cast virtual
char for return void
char16_t friend short volatile
char32_t goto signed wchar_t
class if sizeof while
compl inline static xor
const int static_assert xor_eq
constexpr long static_cast override∗
const_cast mutable struct final∗
continue namespace switch
decltype new template
∗ Note: context sensitive
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 58
Section 2.3.1
Preprocessor
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 59
The Preprocessor
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 60
Source-File Inclusion
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 61
Defining Macros
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 62
Conditional Compilation
4 #endif directive
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 63
Preprocessor Predicate __has_include
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 64
Section 2.3.2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 65
Fundamental Types
boolean type: bool
character types:
char (may be signed or unsigned)
signed char
unsigned char
char16_t
char32_t
wchar_t
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 66
Fundamental Types (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 68
Character Literals
character literal consists of optional prefix followed by one or more
characters enclosed in single quotes
type of character literal determined by prefix (or lack thereof) as follows:
Prefix Literal Type
None ordinary normally char (in special cases int)
u8 UTF-8 char
u UCS-2 char16_t
U UCS-4 char32_t
L wide wchar_t
special characters can be represented by escape sequence:
Escape
Escape
Character Sequence
Character Sequence
newline (LF) \n
question mark (?) \?
horizontal tab (HT) \t
single quote (’) \’
vertical tab (VT) \v
double quote (") \"
backspace (BS) \b
octal number ooo \ooo
carriage return (CR) \r
hex number hhh \xhhh
form feed (FF) \f
code point nnnn \unnnn
alert (BEL) \a
code point nnnnnnnn \Unnnnnnnn
backslash (\) \\
examples of character literals:
’a’ ’1’ ’!’ ’\n’ u’a’ U’a’ L’a’ u8’a’
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 69
Character Literals (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 70
String Literals
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 72
Integer Literals
can be specified in decimal, binary, hexadecimal, and octal
number base indicated by prefix (or lack thereof) as follows:
Prefix Number Base
None decimal
Leading 0 octal
0b or 0B binary
0x or 0X hexadecimal
various suffixes can be specified to control type of literal:
u or U
l or L
both u or U and l or L
ll or LL
both u or U and ll or LL
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 74
Floating-Point Literals
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 75
Hexadecimal Floating-Point Literals
2 hexadecimal digits for integer part of number (optional if at least one digit
after radix point)
3 period character (i.e., radix point)
4 hexadecimal digits for fractional part of number (optional if at least one digit
before radix point)
5 p character (which designates exponent to follow)
6 one or more decimal digits for base-16 exponent
7 optional floating-point literal suffix (e.g., f or l)
examples of hexadecimal floating-point literals:
Literal Type Value (Decimal)
0x.8p0 double 0.5
0x10.cp0 double 16.75
0x.8p0f float 0.5
0xf.fp0f float 15.9375
0x1p10L long double 1024
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 76
Boolean and Pointer Literals
boolean literals:
true
false
pointer literal:
nullptr
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 77
Declarations and Definitions
declaration introduces identifier for type, object (i.e., variable), or function
(without necessarily providing full information about identifier)
in case of object, specifies type (of object)
in case of function, specifies number of parameters, type of each
parameter, and type of return value (if not automatically deduced)
each identifier must be declared before it can be used (i.e., referenced)
definition provides full information about identifier and causes entity
associated with identifier (if any) to be created
in case of type, provides full details about type
in case of object, causes storage to be allocated for object and object to be
created
in case of function, provides code for function body
in case of objects, in most (but not all) contexts, declaring object also
defines it
can declare identifier multiple times but can define only once
above terminology often abused, with “declaration” and “definition” being
used interchangeably
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 78
Examples of Declarations and Definitions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 79
Variable Declarations and Definitions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 80
Arrays
array is collection of one or more objects of same type that are stored
contiguously in memory
each element in array identified by (unique) integer index, with indices
starting from zero
array denoted by []
example:
double x[10]; // array of 10 doubles
int data[512][512]; // 512 by 512 array of ints
elements of array accessed using subscripting operator []
example:
int x[10];
// elements of arrays are x[0], x[1], ..., x[9]
often preferable to use user-defined type for representing array instead of
array type
for example, std::array and std::vector types (to be discussed later)
have numerous practical advantages over array types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 81
Array Example
code:
int a[4] = {1, 2, 3, 4};
assumptions (for some completely fictitious C++ language
implementation):
sizeof(int) is 4
memory layout:
Address Name
1000 1 a[0]
1004 2 a[1]
1008 3 a[2]
1012 4 a[3]
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 82
Pointers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 83
Pointer Example
code:
int i = 42;
int* p = &i;
assert(*p == 42);
assumptions (for some completely fictitious C++ language
implementation):
sizeof(int) is 4
sizeof(int*) is 4
&i is ((int*)1000)
&p is ((int*)1004)
memory layout:
Address Name
1000 42 i
1004 1000 p
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 84
References
code:
int i = 42;
int& j = i;
assert(j == 42);
assumptions (for some completely fictitious C++ language
implementation):
sizeof(int) is 4
&i is ((int*)1000)
memory layout:
Address Name
1000 42 i, j
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 86
References Versus Pointers
references and pointers similar in that both can be used to refer to some
other entity (e.g., object or function)
two key differences between references and pointers:
1 reference must refer to something, while pointer can have null value
(nullptr)
2 references cannot be rebound, while pointers can be changed to point to
different entity
references have cleaner syntax than pointers, since pointers must be
dereferenced upon each use (and dereference operations tend to clutter
code)
use of pointers often implies need for memory management (i.e., memory
allocation, deallocation, etc.), and memory management can introduce
numerous kinds of bugs when done incorrectly
often faced with decision of using pointer or reference in code
generally advisable to prefer use of references over use of pointers unless
compelling reason to do otherwise, such as:
must be able to handle case of referring to nothing
must be able to change entity being referred to
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 87
Unscoped Enumerations
enumerated type provides way to describe range of values that are
represented by named constants called enumerators
object of enumerated type can take any one of enumerators as value
enumerator values represented by some integral type
enumerator can be assigned specific value (which may be negative)
if enumerator not assigned specific value, value defaults to zero if first
enumerator in enumeration and one greater than value for previous
enumerator otherwise
example:
enum Suit {
Clubs, Diamonds, Hearts, Spades
};
Suit suit = Clubs;
example:
enum Suit {
Clubs = 1, Diamonds = 2, Hearts = 4, Spades = 8
};
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 88
Scoped Enumerations
scoped enumeration similar to unscoped enumeration, except
all enumerators are placed in scope of enumeration itself
integral type to used to hold enumerator values can be explicitly specified
conversions involving scoped enumerations are stricter (i.e., more type
safe)
class or struct added after enum keyword to make enumeration
scoped
scope resolution operator (i.e., “::”) used to access enumerators
scoped enumerations should probably be preferred to unscoped ones
example:
enum struct Season {
spring, summer, fall, winter
};
enum struct Suit : unsigned char {
clubs, diamonds, hearts, spades
};
Season season = Season::summer;
Suit suit = Suit::spades;
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 89
Type Aliases with typedef Keyword
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 90
Type Aliases with using Statement
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 91
The extern Keyword
translation unit: basic unit of compilation in C++ (i.e., single source code
file plus all of its directly and indirectly included header files)
extern keyword used to declare object/function in separate translation
unit
example:
extern int evil_global_variable;
// declaration only
// actual definition in another file
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 92
The const Qualifier
const qualifier specifies that object has value that is constant (i.e.,
cannot be changed)
qualifier that applies to object itself said to be top level
following defines x as int with value 42 that cannot be modified:
const int x = 42;
example:
const int x = 42;
x = 13; // ERROR: x is const
const int& x1 = x; // OK
const int* p1 = &x; // OK
int& x2 = x; // ERROR: x const, x2 not const
int* p2 = &x; // ERROR: x const, *p2 not const
example:
int x = 0;
const int& y = x;
x = 42; // OK
// y also changed to 42 since y refers to x
// y cannot be used to change x, however
// i.e., the following would cause compile error:
// y = 24; // ERROR: y is const
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 93
Example: const Qualifier and Non-Pointer/Non-Reference
Types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 94
The const Qualifier and Pointer Types
every pointer is associated with two objects: pointer itself and pointee
(i.e., object to which pointer points)
const qualifier can be applied to each of pointer (i.e., top-level qualifier)
and pointee
Address
..
int i = 42; // pointee .
1000 (pointer)
// p is pointer to int i 2000
// for example: (&p)
// int* p = &i; ..
// const int* p = &i; .
// int* const p = &i;
// const int* const p = &i; 2000 42
(&i) (pointee)
..
.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 95
Example: const Qualifier and Pointer Types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 96
The const Qualifier and Reference Types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 97
Example: const Qualifier and Reference Types
// with reference types, const can only be applied to referee
// reference itself cannot be rebound (i.e., is constant)
// referee may be const or non-const
int i = 0; const int ci = 0;
int i1 = 0; const int ci1 = 0;
// reference to non-const int
int& ri = i;
ri = ci; // OK: can modify non-const referee
int& ri = i1; // ERROR: cannot redefine/rebind reference
// reference to const int
const int& rci = ci;
rci = i; // ERROR: cannot modify const referee
const int& rci = ci1;
// ERROR: cannot redefine/rebind reference
// ERROR: reference itself cannot be const qualified
int& const cri = i; // ERROR: invalid const qualifier
// ERROR: reference itself cannot be const qualified
const int& const crci = ci; // ERROR: invalid const qualifier
// also: int const& const crci = ci; // ERROR
const int& r1 = ci; // OK: adds const to referee
int& r2 = ci; // ERROR: discards const from referee
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 98
The const Qualifier and Pointer-to-Pointer Types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 99
The volatile Qualifier
volatile qualifier used to indicate that object can change due to agent
external to program (e.g., memory-mapped device, signal handler)
compiler cannot optimize away read and write operations on volatile
objects (e.g., repeated reads without intervening writes cannot be
optimized away)
volatile qualifier typically used when object:
corresponds to register of memory-mapped device
may be modified by signal handler (namely, object of type
volatile std::sig_atomic_t)
example:
volatile int x;
volatile unsigned char* deviceStatus;
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 100
The auto Keyword
in various contexts, auto keyword can be used as place holder for type
in such contexts, implication is that compiler must deduce type
example:
auto i = 3; // i has type int
auto j = i; // j has type int
auto& k = i; // k has type int&
const auto& n = i; // n has type const int&
auto x = 3.14; // x has type double
very useful in generic programming (covered later) when types not always
easy to determine
can potentially save typing long type names
can lead to more readable code (if well used)
if overused, can lead to bugs (sometimes very subtle ones) and difficult to
read code
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 101
Inline Variables
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 102
Inline Variable: Example
inline_variable_1_1.hpp
1 inline int magic = 42;
main.cpp
1 #include <iostream>
2 #include "inline_variable_1_1.hpp"
3 int main() {
4 std::cout << magic << "\n";
5 }
other.cpp
1 #include "inline_variable_1_1.hpp"
2 void func() {/* ... */}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 103
Section 2.3.3
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 104
Operators
Arithmetic Operators
Operator Name Syntax
addition a + b Bitwise Operators
subtraction a - b
Operator Name Syntax
unary plus +a
bitwise NOT ˜a
unary minus -a
bitwise AND a & b
multiplication a * b
bitwise OR a | b
division a / b
bitwise XOR a ˆ b
modulo (i.e., remainder) a % b
arithmetic left shift a << b
pre-increment ++a
arithmetic right shift a >> b
post-increment a++
pre-decrement --a
post-decrement a--
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 105
Operators (Continued 1)
Assignment and
Compound-Assignment Operators
Operator Name Syntax
assignment a = b
addition assignment a += b
subtraction assignment a -= b
multiplication assignment a *= b
division assignment a /= b
modulo assignment a %= b
bitwise AND assignment a &= b
bitwise OR assignment a |= b
bitwise XOR assignment a ˆ= b
arithmetic left shift assignment a <<= b
arithmetic right shift assignment a >>= b
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 106
Operators (Continued 2)
Logical/Relational Operators
Operator Name Syntax Member and Pointer Operators
equal a == b Operator Name Syntax
not equal a != b array subscript a[b]
greater than a > b indirection *a
less than a < b address of &a
greater than or equal a >= b member selection a.b
less than or equal a <= b member selection a->b
logical negation !a member selection a.*b
logical AND a && b member selection a->*b
logical OR a || b
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 107
Operators (Continued 3)
Other Operators
Operator Name Syntax
function call a(...)
comma a, b
ternary conditional a ? b : c
scope resolution a::b
sizeof sizeof(a)
parameter-pack sizeof sizeof...(a)
alignof alignof(T)
allocate storage new T
allocate storage (array) new T[a]
deallocate storage delete a
deallocate storage (array) delete[] a
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 108
Operators (Continued 4)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 109
Operator Precedence
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 110
Operator Precedence (Continued 1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 111
Operator Precedence (Continued 2)
Precedence Operator Name Associativity
4 .* member selection (objects) left to right
->* member selection (pointers)
5 * multiplication left to right
/ division
% modulus
6 + addition left to right
- subtraction
7 << left shift left to right
>> right shift
8 < less than left to right
<= less than or equal
> greater than
>= greater than or equal
9 == equality left to right
!= inequality
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 112
Operator Precedence (Continued 3)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 113
Operator Precedence (Continued 4)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 114
Alternative Tokens
Alternative Primary
and &&
bitor |
or ||
xor ˆ
compl ˜
bitand &
and_eq &=
or_eq |=
xor_eq ˆ=
not !
not_eq !=
alternative tokens above probably best avoided as they lead to more
verbose code
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 115
Expressions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 116
Operator Precedence/Associativity Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 117
Short-Circuit Evaluation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 118
The static_assert Statement
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 119
The sizeof Operator
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 120
The alignof Operator
object type can have restriction on address at which object of type can
start called alignment requirement
for given object type T, starting address for objects of type T must be
integer multiple of N bytes, where integer N is called alignment of type
alignment of 1 corresponds to no restriction on alignment (since starting
address of object can be any address in memory)
alignment of 2 restricts starting address of object to be even (i.e., integer
multiple of 2)
for efficiency reasons and due to restrictions imposed by hardware,
alignment of particular type may be greater than 1
alignof operator is used to query alignment of type
for object type T, alignof(T) yields alignment used for objects of this
type
alignof(char), alignof(signed char), and
alignof(unsigned char) guaranteed to be 1
fundamental types of size greater than 1 often have alignment greater
than 1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 121
The alignas Specifier
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 122
The constexpr Qualifier for Variables
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 123
Section 2.3.4
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 124
The if Statement
allows conditional execution of code
syntax has form:
if (expression)
statement1
else
statement2
if expression expression is true, execute statement statement1 ; otherwise,
execute statement statement2
else clause can be omitted leading to simpler form:
if (expression)
statement1
conditional execution based on more than one condition can be achieved
using construct like:
if (expression1 )
statement1
else if (expression2 )
statement2
...
else
statementn
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 125
The if Statement (Continued 1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 126
The if Statement (Continued 2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 129
The switch Statement
allows conditional execution of code based on integral/enumeration value
syntax has form:
switch (expression) {
case const expr1 :
statements1
case const expr2 :
statements2
...
case const exprn :
statementsn
default:
statements
}
expression is expression of integral or enumeration type or implicitly
convertible to such type; const expri is constant expression of same type
as expression after conversions/promotions
if expression expression equals const expri , jump to beginning of
statements statementsi ; if expression expr does not equal const expri for
any i, jump to beginning of statements statements
then, continue executing statements until break statement is
encountered
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 130
The switch Statement (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 131
The switch Statement: Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 132
The switch Statement: Example (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 133
The while Statement
looping construct
syntax has form:
while (expression)
statement
advisable to always use brace brackets, even when loop body consists of
only one statement
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 134
The while Statement: Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 135
The for Statement
looping construct
has following syntax:
for (statement1 ; expression; statement2 )
statement3
first, execute statement statement1 ; then, while expression expression is
true, execute statement statement3 followed by statement statement2
statement1 and statement2 may be omitted; expression treated as true if
omitted
to include multiple statements in loop body, must group multiple
statements into single statement using brace brackets; advisable to always
use brace brackets, even when loop body consists of only one statement:
for (statement1 ; expression; statement2 ) {
statement3,1
statement3,2
...
}
any objects declared in statement1 go out of scope as soon as for loop
ends
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 136
The for Statement (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 137
The for Statement: Example
example with single statement in loop body:
// Print the integers from 0 to 9 inclusive.
for (int i = 0; i < 10; ++i)
std::cout << i << ’\n’;
example with multiple statements in loop body:
int values[10];
// ...
int sum = 0;
for (int i = 0; i < 10; ++i) {
// Stop if value is negative.
if (values[i] < 0) {
break;
}
sum += values[i];
}
example with error in assumption about scoping rules:
for (int i = 0; i < 10; ++i) {
std::cout << i << ’\n’;
}
++i; // ERROR: i no longer exists
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 138
Range-Based for Statement
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 139
The do Statement
looping construct
has following general syntax:
do
statement
while (expression);
statement statement executed;
then, expression expression evaluated;
if expression expression is true, entire process repeats from beginning
to execute multiple statements in body of loop, must group multiple
statements into single statement using brace brackets
do {
statement1
statement2
...
} while (expression);
advisable to always use brace brackets, even when loop body consists of
only one statement
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 140
The do Statement: Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 141
The break Statement
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 142
The continue Statement
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 143
The goto Statement
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 144
Section 2.3.5
Functions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 145
Function Parameters, Arguments, and Return Values
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 146
Function Declarations and Definitions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 147
Basic Syntax (Leading Return Type)
most basic syntax for function declarations and definitions places return
type at start (i.e., leading return-type syntax)
basic syntax for function declaration:
return type function name(parameter declarations);
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 148
Trailing Return-Type Syntax
with trailing return-type syntax, return type comes after parameter
declarations and auto used as placeholder for where return type would
normally be placed
trailing return-type syntax for function declaration:
auto function name(parameter declarations) -> return type;
examples of function declarations:
auto min(int, int) -> int;
auto square(double) -> double;
trailing return-type syntax for function definition:
auto function name(parameter declarations) -> return type
{
statements
}
examples of function definitions:
auto min(int x, int y) -> int
{return x < y ? x : y;}
auto square(double x) -> double {return x * x;}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 149
The return Statement
return statement used to exit function, passing specified return value (if
any) back to caller
code in function executes until return statement is reached or execution
falls off end of function
if function return type is not void, return statement takes single
parameter indicating value to be returned
if function return type is void, function does not return any value and
return statement takes either no parameter or expression of type void
falling off end of function equivalent to executing return statement with
no value
example:
double unit_step(double x) {
if (x >= 0.0) {
return 1.0; // exit with return value 1.0
}
return 0.0; // exit with return value 0.0
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 150
Automatic Return-Type Deduction
with both leading and trailing return-type syntax, can specify return type
as auto
in this case, return type of function will be automatically deduced
if function definition has no return statement, return type deduced to be
void
otherwise, return type deduced to match type in expression of return
statement or, if return statement has no expression, as void
if multiple return statements, must use same type for all return
expressions
when return-type deduction used, function definition must be visible in
order to call function (since return type cannot be determined otherwise)
example:
auto square(double x) {
return x * x;
// x * x has type double
// deduced return type is double
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 151
The main Function
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 152
The main Function (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 153
Lifetime
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 154
Parameter Passing
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 155
Pass-By-Value Versus Pass-By-Reference
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 156
Increment Example: Incorrectly Using Pass By Value
consider code:
1 void increment(int x) {
2 ++x;
3 }
4
5 void func() {
6 int i = 0;
7 increment(i); // i is not modified
8 // i is still 0
9 }
when func calls increment, parameter passing copies value of i in func
to local variable x in increment:
i in Copy x in
func increment
Value
0 0
when code in increment executes, local variable x is incremented (not i
in func):
i in x in
func increment
0 1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 157
Increment Example: Correctly Using Pass By Reference
consider code:
1 void increment(int& x) {
2 ++x;
3 }
4
5 void func() {
6 int i = 0;
7 increment(i); // i is incremented
8 // i is now 1
9 }
when func calls increment, reference x in increment is bound to object
i in func (i.e., x becomes alias for i):
i in func
and
x in increment
0
when code in increment executes, x is incremented, which is alias for i
in func:
i in func
and
x in increment
1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 158
The const Qualifier and Functions
const qualifier can be used in function declaration to make promises
about what non-local objects will not be modified by function
for function parameter of pointer type, const-ness of pointed-to object (i.e.,
pointee) extremely important
if pointee is const, function promises not to change pointee; for example:
int strlen(const char*); // get string length
for function parameter of reference type, const-ness of referred-to object
(i.e., referee) extremely important
if referee is const, function promises not to change referee; for example:
std::complex<double>
square(const std::complex<double>&);
// compute square of number
not making appropriate choice of const-ness for pointed-to or referred-to
object will result in fundamentally incorrect code
if function will never modify pointee/referee associated with function
parameter, parameter type should be made pointer/reference to const
object
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 159
String Length Example: Not Const Correct
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 160
String Length Example: Const Correct
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 161
Square Example: Not Const Correct
1 #include <complex>
2
3 using Complex = std::complex<long double>;
4
5 // ERROR: parameter type should be reference to const
6 Complex square(Complex& z) {
7 return z * z;
8 }
9
10 int main() {
11 const Complex c1(1.0, 2.0);
12 Complex c2(1.0, 2.0);
13 Complex r1 = square(c1);
14 // must bind parameter z to argument c1
15 // Complex& z = c1;
16 // convert from const Complex to Complex&
17 // ERROR: must discard const from referee
18 Complex r2 = square(c2);
19 // must bind parameter z to argument c2
20 // Complex& z = c2;
21 // convert from Complex to Complex&
22 // OK: constness of referee unchanged
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 162
Square Example: Const Correct
1 #include <complex>
2
3 using Complex = std::complex<long double>;
4
5 // OK: parameter type is reference to const
6 Complex square(const Complex& z) {
7 return z * z;
8 }
9
10 int main() {
11 const Complex c1(1.0, 2.0);
12 Complex c2(1.0, 2.0);
13 Complex r1 = square(c1);
14 // must bind parameter z to argument c1
15 // const Complex& z = c1;
16 // convert from const Complex to const Complex&
17 // OK: constness of referee not discarded
18 Complex r2 = square(c2);
19 // must bind parameter z to argument c2
20 // const Complex& z = c2;
21 // convert from Complex to const Complex&
22 // OK: can add const to referee
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 163
Function Types and the const Qualifier
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 164
Inline Functions
in general programming sense, inline function is function for which
compiler copies code from function definition directly into code of calling
function rather than creating separate set of instructions in memory
since code copied directly into calling function, no need to transfer control
to separate piece of code and back again to caller, eliminating
performance overhead of function call
inline typically used for very short functions (where overhead of calling
function is large relative to cost of executing code within function itself)
can request function be made inline by including inline qualifier along
with function return type (but compiler may ignore request)
inline function must be defined in each translation unit in which function is
used and all definitions must be identical; this is exception to
one-definition rule
example:
inline bool isEven(int x) {
return x % 2 == 0;
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 165
Inlining of a Function
Code fragment 2:
void myFunction() {
int i = 3;
bool result = (i % 2 == 0);
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 166
The constexpr Qualifier for Functions
constexpr qualifier indicates return value of function is constant
expression (i.e., can be evaluated at compile time) provided that all
arguments to function are constant expressions
constexpr function required to be evaluated at compile time if all
arguments are constant expressions and return value used in constant
expression
constexpr functions are implicitly inline
constexpr function very restricted in what it can do (e.g., no external state,
can only call constexpr functions, variables must be initialized)
example:
constexpr int factorial(int n) {
return n >= 2 ? (n * factorial(n - 1)) : 1;
}
int u[factorial(5)];
// OK: factorial(5) is constant expression
int x = 5;
int v[factorial(x)];
// ERROR: factorial(x) is not constant
// expression
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 167
Constexpr Function Example: square
1 #include <iostream>
2
3 constexpr double square(double x) {
4 return x * x;
5 }
6
7 int main() {
8 constexpr double a = square(2.0);
9 // must be computed at compile time
10
11 double b = square(0.5);
12 // might be computed at compile time
13
14 double t;
15 if (!(std::cin >> t)) {
16 return 1;
17 }
18 const double c = square(t);
19 // must be computed at run time
20
21 std::cout << a << ’ ’ << b << ’ ’ << c << ’\n’;
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 168
Constexpr Function Example: power_int (Recursive)
1 #include <iostream>
2
3 constexpr double power_int_helper(double x, int n) {
4 return (n > 0) ? x * power_int_helper(x, n - 1) : 1;
5 }
6
7 constexpr double power_int(double x, int n) {
8 return (n < 0) ? power_int_helper(1.0 / x, -n) :
9 power_int_helper(x, n);
10 }
11
12 int main() {
13 constexpr double a = power_int(0.5, 8);
14 // must be computed at compile time
15
16 double b = power_int(0.5, 8);
17 // might be computed at compile time
18
19 double x;
20 if (!(std::cin >> x)) {return 1;}
21 const double c = power_int(x, 2);
22 // must be computed at run time
23
24 std::cout << a << ’ ’ << b << ’ ’ << c << ’\n’;
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 169
Constexpr Function Example: power_int (Iterative)
1 #include <iostream>
2
3 constexpr double power_int(double x, int n) {
4 double result = 1.0;
5 if (n < 0) {
6 x = 1.0 / x;
7 n = -n;
8 }
9 while (--n >= 0) {
10 result *= x;
11 }
12 return result;
13 }
14
15 int main() {
16 constexpr double a = power_int(0.5, 8);
17 // must be computed at compile time
18
19 double b = power_int(0.5, 8);
20 // might be computed at compile time
21
22 double x;
23 if (!(std::cin >> x)) {return 1;}
24 const double c = power_int(x, 2);
25 // must be computed at run time
26
27 std::cout << a << ’ ’ << b << ’ ’ << c << ’\n’;
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 170
Compile-Time Versus Run-Time Computation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 171
Function Overloading
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 172
Default Arguments
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 173
Argument Matching
call of given function name chooses function that best matches actual
arguments
consider all functions in scope for which set of conversions exists so
function could possibly be called
best match is intersection of sets of functions that best match on each
argument
matches attempted in following order:
1 exact match with zero or more trivial conversions (e.g., T to T&, T& to T,
adding const and/or volatile); of these, those that do not add const
and/or volatile to pointer/reference better than those that do
2 match with promotions (e.g., int to long, float to double)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 175
The assert Macro
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 176
Section 2.3.6
Input/Output (I/O)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 177
Basic I/O
1 #include <iostream>
2
3 int main() {
4 std::cout << "Enter an integer: ";
5 int x;
6 std::cin >> x;
7 if (std::cin) {
8 std::cout << "The integer entered was "
9 << x << ".\n";
10 } else {
11 std::cerr <<
12 "End-of-file reached or I/O error.\n";
13 }
14 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 179
I/O Manipulators
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 180
I/O Manipulators (Continued)
Name Description
setw set field width
setfill set fill character
endl insert newline and flush
flush flush stream
dec use decimal
hex use hexadecimal
oct use octal
showpos show positive sign
noshowpos do not show positive sign
left left align
right right align
fixed write floating-point values in fixed-point notation
scientific write floating-point values in scientific notation
setprecision for default notation, specify maximum number of mean-
ingful digits to display before and after decimal point; for
fixed and scientific notations, specify exactly how many
digits to display after decimal point (padding with trail-
ing zeros if necessary)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 181
I/O Manipulators Example
1 #include <iostream>
2 #include <ios>
3 #include <iomanip>
4
5 int main() {
6 constexpr double pi = 3.1415926535;
7 constexpr double big = 123456789.0;
8 // default notation
9 std::cout << pi << ’ ’ << big << ’\n’;
10 // fixed-point notation
11 std::cout << std::fixed << pi << ’ ’ << big << ’\n’;
12 // scientific notation
13 std::cout << std::scientific << pi << ’ ’ << big << ’\n’;
14 // fixed-point notation with 7 digits after decimal point
15 std::cout << std::fixed << std::setprecision(7) << pi << ’ ’
16 << big << ’\n’;
17 // fixed-point notation with precision and width specified
18 std::cout << std::setw(8) << std::fixed << std::setprecision(2)
19 << pi << ’ ’ << std::setw(20) << big << ’\n’;
20 // fixed-point notation with precision, width, and fill specified
21 std::cout << std::setw(8) << std::setfill(’x’) << std::fixed
22 << std::setprecision(2) << pi << ’ ’ << std::setw(20) << big << ’\n’;
23 }
24
25 /* This program produces the following output:
26 3.14159 1.23457e+08
27 3.141593 123456789.000000
28 3.141593e+00 1.234568e+08
29 3.1415927 123456789.0000000
30 3.14 123456789.00
31 xxxx3.14 xxxxxxxx123456789.00
32 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 182
Section 2.3.7
Miscellany
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 183
Namespaces
namespace is region that provides scope for identifiers declared inside
namespace provides mechanism for reducing likelihood of naming
conflicts
syntax for namespace has general form:
namespace name {
body
}
name: identifier that names namespace
body: body of namespace (i.e., code)
all identifiers (e.g., names of variables, functions, and types) declared in
body made to belong to scope associated with namespace name
same identifier can be re-used in different namespaces, since each
namespace is separate scope
scope-resolution operator (i.e., ::) can be used to explicitly specify
namespace to which particular identifier belongs
using statement can be used to bring identifiers from other namespaces
into current scope
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 184
Namespaces: Example
1 #include <iostream>
2
3 using std::cout; // bring std::cout into current scope
4
5 namespace mike {
6 int someValue;
7 void initialize() {
8 cout << "mike::initialize called\n";
9 someValue = 0;
10 }
11 }
12
13 namespace fred {
14 double someValue;
15 void initialize() {
16 cout << "fred::initialize called\n";
17 someValue = 1.0;
18 }
19 }
20
21 void func() {
22 mike::initialize(); // call initialize in namespace mike
23 fred::initialize(); // call initialize in namespace fred
24 using mike::initialize;
25 // bring mike::initialize into current scope
26 initialize(); // call mike::initialize
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 185
Nested Namespace Definitions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 186
Namespace Aliases
identifier can be introduced as alias for namespace
syntax has following form:
namespace alias name = ns name;
identifier alias name is alias for namespace ns name
namespace aliases particularly useful for creating short names for
deeply-nested namespaces or namespaces with long names
example:
1 #include <iostream>
2
3 namespace foobar {
4 namespace miscellany {
5 namespace experimental {
6 int get_meaning_of_life() {return 42;}
7 void greet() {std::cout << "hello\n";};
8 }
9 }
10 }
11
12 int main() {
13 namespace n = foobar::miscellany::experimental;
14 n::greet();
15 std::cout << n::get_meaning_of_life() << ’\n’;
16 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 187
Inline Namespaces
namespace can be made inline, in which case all identifiers in namespace
also visible in enclosing namespace
inline namespaces useful, for example, for library versioning
example:
1 #include <cassert>
2
3 // some awesome library
4 namespace awesome {
5 // version 1
6 namespace v1 {
7 int meaning_of_life() {return 41;}
8 }
9 // new and improved version 2
10 // which should be default for library users
11 inline namespace v2 {
12 int meaning_of_life() {return 42;}
13 }
14 }
15
16 int main() {
17 assert(awesome::v1::meaning_of_life() == 41);
18 assert(awesome::v2::meaning_of_life() == 42);
19 assert(awesome::meaning_of_life() == 42);
20 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 188
Unnamed Namespaces
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 189
Memory Allocation: new and delete
to allocate memory, use new statement
to deallocate memory allocated with new statement, use delete
statement
similar to malloc and free in C
two forms of allocation: 1) single object (i.e., nonarray case) and 2) array
of objects
array version of new/delete distinguished by []
example:
char* buffer = new char[64]; // allocate
// array of 64 chars
delete [] buffer; // deallocate array
double* x = new double; // allocate single double
delete x; // deallocate single object
important to match nonarray and array versions of new and delete:
char* buffer = new char[64]; // allocate
delete buffer; // ERROR: nonarray delete to
// delete array
// may compile fine, but crash
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 190
User-Defined Literals
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 191
Attributes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 192
Some Standard Attributes
Name Description
noreturn function does not return
deprecated use of entity is deprecated (i.e., allowed but
discouraged)
fallthrough fall through in switch statement is deliberate
maybe_unused entity (e.g., variable) may be unused
nodiscard used to indicate that return value of function
should not be ignored
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 193
Some GCC and Clang Attributes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 194
Section 2.3.8
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 195
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 196
Section 2.4
Classes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 197
Classes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 198
Section 2.4.1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 199
Class Members
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 200
Access Specifiers
can control level of access that users of class have to its members
three levels of access:
1 public
2 protected
3 private
public: member can be accessed by any code
private: member can only be accessed by other members of class and
friends of class (to be discussed shortly)
protected: relates to inheritance (discussion deferred until later)
public members constitute class interface
private members constitute class implementation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 201
Class Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 202
Default Member Access
class Widget {
// ...
};
class Widget {
private:
// ...
};
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 203
The struct Keyword
struct Widget {
// ...
};
class Widget {
public:
// ...
};
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 204
Data Members
class example:
class Vector_2 { // Two-dimensional vector class.
public:
double x; // The x component of the vector.
double y; // The y component of the vector.
};
void func() {
Vector_2 v;
v.x = 1.0; // Set data member x to 1.0
v.y = 2.0; // Set data member y to 2.0
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 205
Function Members
class example:
class Vector_2 { // Two-dimensional vector class.
public:
void initialize(double newX, double newY);
double x; // The x component of the vector.
double y; // The y component of the vector.
};
void Vector_2::initialize(double newX, double newY) {
x = newX; // "x" means "this->x"
y = newY; // "y" means "this->y"
}
void func() {
Vector_2 v; // Create Vector_2 called v.
v.initialize(1.0, 2.0); // Initialize v to (1.0, 2.0).
}
above class has member function initialize
to refer to member of class outside of class body must use
scope-resolution operator (i.e., ::)
for example, in case of initialize function, we use
Vector_2::initialize
member function always has implicit parameter referring to class object
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 206
The this Keyword
member function always has implicit parameter referring to class object
implicit parameter accessible inside member function via this keyword
this is pointer to object for which member function is being invoked
data members can be accessed through this pointer
since data members can also be referred to directly by their names,
explicit use of this often not needed and normally avoided
example:
class Widget {
public:
int updateValue(int newValue) {
int oldValue = value; // "value" means "this->value"
value = newValue; // "value" means "this->value"
return oldValue;
}
private:
int value;
};
void func() {
Widget x;
x.updateValue(5);
// in Widget::updateValue, variable this equals &x
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 207
const Member Functions
member function has reference to object of class as implicit parameter
(i.e., object pointed to by this)
need way to indicate if member function can change value of object
const member function cannot change value of object
1 class Counter {
2 public:
3 int getCount() const
4 {return count;} // count means this->count
5 void setCount(int newCount)
6 {count = newCount;} // count means this->count
7 void incrementCount()
8 {++count;} // count means this->count
9 private:
10 int count; // counter value
11 };
12
13 void func() {
14 Counter ctr;
15 ctr.setCount(0);
16 int count = ctr.getCount();
17 const Counter& ctr2 = ctr;
18 count = ctr2.getCount(); // getCount better be const!
19 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 208
Definition of Function Members in Class Body
class MyInteger {
public:
// Set the value of the integer and return the old value.
int setValue(int newValue);
private:
int value;
};
inline int MyInteger::setValue(int newValue) {
int oldValue = value;
value = newValue;
return oldValue;
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 209
Type Members
example:
class Point_2 { // Two-dimensional point class.
public:
using Coordinate = double; // Coordinate type.
Coordinate x; // The x coordinate of the point.
Coordinate y; // The y coordinate of the point.
};
void func() {
Point_2 p;
// ...
Point_2::Coordinate x = p.x;
// Point_2::Coordinate same as double
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 210
Friends
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 212
Section 2.4.2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 213
Propagating Values: Copying and Moving
Suppose that we have two objects of the same type and we want to
propagate the value of one object (i.e., the source) to the other object (i.e.,
the destination).
This can be accomplished in one of two ways: 1) copying or 2) moving.
Copying propagates the value of the source object to the destination
object without modifying the source object.
Moving propagates the value of the source object to the destination
object and is permitted to modify the source object.
Moving is always at least as efficient as copying, and for many types,
moving is more efficient than copying.
For some types, copying does not make sense, while moving does (e.g.,
std::ostream, std::istream).
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 214
Copying and Moving
Copy operation. Propagating the value of the source object source to the
destination object destination by copying.
source destination source destination
a b a a
A copy operation does not modify the value of the source object.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 217
Copy Constructor
for class T, constructor taking lvalue reference to T as first parameter that
can be called with one argument known as copy constructor
used to create object by copying from already-existing object
copy constructor for class T typically is of form T(const T&)
if no copy constructor specified (and no move constructor or move
assignment operator specified), copy constructor is automatically
provided that copies each data member (using copy constructor for class
and bitwise copy for built-in type)
class Vector { // Two-dimensional vector class.
public:
Vector() {x_ = 0.0; y_ = 0.0;} // Default constructor
Vector(const Vector& v) // Copy constructor.
{x_ = v.x_; y_ = v.y_;}
// ...
private:
double x_; // The x component of the vector.
double y_; // The y component of the vector.
};
Vector v;
Vector w(v); // calls Vector::Vector(const Vector&)
Vector u = v; // calls Vector::Vector(const Vector&)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 218
Move Constructor
for class T, constructor taking rvalue reference to T as first parameter that
can be called with one argument known as move constructor
used to create object by moving from already-existing object
move constructor for class T typically is of form T(T&&)
if no move constructor specified (and no destructor, copy constructor, or
copy/move assignment operator specified), move constructor is
automatically provided that moves each data member (using move for
class and bitwise copy for built-in type)
class Vector { // Two-dimensional vector class.
public:
Vector() {x_ = 0.0; y_ = 0.0;} // Default constructor
Vector(Vector&& v) {x_ = v.x_; y_ = v.y_;} // Move constructor.
// ...
private:
double x_; // The x component of the vector.
double y_; // The y component of the vector.
};
#include <utility>
Vector v;
Vector w(std::move(v)); // calls Vector::Vector(Vector&&)
Vector x = std::move(w); // calls Vector::Vector(Vector&&)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 219
Constructor Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 220
Constructor Example (Continued 1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 221
Constructor Example (Continued 2)
1 #include <utility>
2 #include <cstdlib>
3 // include definition of Vector class here
4
5 // named RVO not possible
6 Vector func1() {
7 Vector a(1.0, 0.0);
8 Vector b(0.0, 1.0);
9 if (std::rand() % 2) {return a;}
10 else {return b;}
11 }
12
13 // RVO required
14 Vector func2() {return Vector(1.0, 1.0);}
15
16 int main() {
17 Vector u(1.0, 1.0);
18 Vector v(std::move(u));
19 // move constructor invoked to propagate value from u
20 // to v
21 Vector w = func1();
22 // move constructor invoked to propagate value of object
23 // in return statement of func1 to object w in main
24 // (named RVO not possible)
25 Vector x = func2();
26 // move constructor not invoked, due to guaranteed
27 // copy/move elision (return value of func2 directly
28 // constructed in object x in main)
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 222
Initializer Lists
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 223
Initializer List Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 224
Destructors
when object reaches end of lifetime, typically some cleanup required
before object passes out of existence
destructor is member function that is automatically called when object
reaches end of lifetime in order to perform any necessary cleanup
often object may have allocated resources associated with it (e.g.,
memory, files, devices, network connections, processes/threads)
when object destroyed, must ensure that any resources associated with
object are released
destructors often serve to release resources associated with object
destructor for class T always has name T::˜T
destructor has no return type (not even void)
destructor cannot be overloaded
destructor always takes no parameters
if no destructor is specified, destructor automatically provided that calls
destructor for each data member of class type
sometimes, automatically provided destructor will not have correct
behavior
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 225
Destructor Example
example:
class Widget {
public:
Widget(int bufferSize) { // Constructor.
// allocate some memory for buffer
bufferPtr_ = new char[bufferSize];
}
˜Widget() { // Destructor.
// free memory previously allocated
delete [] bufferPtr_;
}
// copy constructor, assignment operator, ...
private:
char* bufferPtr_; // pointer to start of buffer
};
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 226
Section 2.4.3
Operator Overloading
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 227
Operator Overloading
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 228
Operator Overloading (Continued 1)
operator @ overloaded via special function named operator@
with some exceptions, operator can be overloaded as member function or
nonmember function
if operator overloaded as member function, first operand provided as
*this and remaining operands, if any, provided as function parameters
if operator overloaded as nonmember function, all operands provided as
function parameters
postfix unary (increment/decrement) operators take additional dummy
parameter of type int in order to distinguish from prefix case
expressions involving overloaded operators interpreted as follows:
Interpretation As
Type Expression Member Function Nonmember Function
Binary a@b a.operator@(b) operator@(a, b)
Prefix unary @a a.operator@() operator@(a)
Postfix unary a@ a.operator@(i) operator@(a, i)
i is dummy parameter of type int
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 229
Operator Overloading (Continued 2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 230
Operator Overloading (Continued 3)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 231
Operator Overloading Example: Vector
1 class Vector { // Two-dimensional vector class
2 public:
3 Vector() : x_(0.0), y_(0.0) {}
4 Vector(double x, double y) : x_(x), y_(y) {}
5 double x() const { return x_; }
6 double y() const { return y_; }
7 private:
8 double x_; // The x component
9 double y_; // The y component
10 };
11
12 // Vector addition
13 Vector operator+(const Vector& u, const Vector& v)
14 {return Vector(u.x() + v.x(), u.y() + v.y());}
15
16 // Dot product
17 double operator*(const Vector& u, const Vector& v)
18 {return u.x() * v.x() + u.y() * v.y();}
19
20 void func() {
21 Vector u(1.0, 2.0);
22 Vector v(u);
23 Vector w;
24 w = u + v; // w.operator=(operator+(u, v))
25 double c = u * v; // calls operator*(u, v)
26 // since c is built-in type, assignment operator
27 // does not require function call
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 232
Operator Overloading Example: Array10
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 233
Operator Overloading: Member vs. Nonmember Functions
some considerations: access to private members; whether first operand
has class type
1 class Complex { // Complex number type.
2 public:
3 Complex(double x, double y) : x_(x), y_(y) {}
4 double real() const {return x_;}
5 double imag() const {return y_;}
6
7 // Alternatively, overload as a member function.
8 // Complex operator+(double b) const
9 // {return Complex(real() + b, imag());}
10 private:
11 double x_; // The real part.
12 double y_; // The imaginary part.
13 };
14
15 // Overload as a nonmember function.
16 // (A member function could instead be used. See above.)
17 Complex operator+(const Complex& a, double b)
18 {return Complex(a.real() + b, a.imag());}
19
20 // This can only be accomplished with a nonmember function.
21 Complex operator+(double b, const Complex& a)
22 {return Complex(b + a.real(), a.imag());}
23
24 void myFunc() {
25 Complex a(1.0, 2.0);
26 Complex b(1.0, -2.0);
27 double r = 2.0;
28 Complex c = a + r; // could use nonmember or member function
29 // operator+(a, r) or a.operator+(r)
30 Complex d = r + a; // must use nonmember function
31 // operator+(r, a)
32 // since r.operator+(a) will not work
33 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 234
Copy Assignment Operator
for class T, T::operator= having exactly one parameter that is lvalue
reference to T known as copy assignment operator
used to assign, to already-existing object, value of another object by
copying
if no copy assignment operator specified (and no move constructor or
move assignment operator specified), copy assignment operator
automatically provided that copy assigns to each data member (using data
member’s copy assignment operator for class and bitwise copy for built-in type)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 236
Move Assignment Operator
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 237
Copy/Move Assignment Operator Example: Complex
1 class Complex {
2 public:
3 Complex(double x = 0.0, double y = 0.0) :
4 x_(x), y_(y) {}
5 Complex(const Complex& a) : x_(a.x_), y_(a.y_) {}
6 Complex(Complex&& a) : x_(a.x_), y_(a.y_) {}
7 Complex& operator=(const Complex& a) { // Copy assign
8 if (this != &a) {
9 x_ = a.x_; y_ = a.y_;
10 }
11 return *this;
12 }
13 Complex& operator=(Complex&& a) { // Move assign
14 x_ = a.x_; y_ = a.y_;
15 return *this;
16 }
17 private:
18 double x_; // The real part.
19 double y_; // The imaginary part.
20 };
21
22 int main() {
23 Complex z(1.0, 2.0);
24 Complex v(1.5, 2.5);
25 v = z; // v.operator=(z)
26 v = Complex(0.0, 1.0); // v.operator=(Complex(0.0, 1.0))
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 238
Assignment Operator Example: Buffer
1 class Buffer { // Character buffer class.
2 public:
3 Buffer(int bufferSize) { // Constructor.
4 bufSize_ = bufferSize;
5 bufPtr_ = new char[bufferSize];
6 }
7 Buffer(const Buffer& buffer) { // Copy constructor.
8 bufSize_ = buffer.bufSize_;
9 bufPtr_ = new char[bufSize_];
10 for (int i = 0; i < bufSize_; ++i)
11 bufPtr_[i] = buffer.bufPtr_[i];
12 }
13 ˜Buffer() { // Destructor.
14 delete [] bufPtr_;
15 }
16 Buffer& operator=(const Buffer& buffer) { // Copy assignment operator.
17 if (this != &buffer) {
18 delete [] bufPtr_;
19 bufSize_ = buffer.bufSize_;
20 bufPtr_ = new char[bufSize_];
21 for (int i = 0; i < bufSize_; ++i)
22 bufPtr_[i] = buffer.bufPtr_[i];
23 }
24 return *this;
25 }
26 // ...
27 private:
28 int bufSize_; // buffer size
29 char* bufPtr_; // pointer to start of buffer
30 };
Miscellany
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 240
std::initializer_list Class Template
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 241
std::initializer_list Example
1 #include <iostream>
2 #include <vector>
3
4 class Sequence {
5 public:
6 Sequence(std::initializer_list<int> list) {
7 for (std::initializer_list<int>::const_iterator i =
8 list.begin(); i != list.end(); ++i)
9 elements_.push_back(*i);
10 }
11 void print() const {
12 for (std::vector<int>::const_iterator i =
13 elements_.begin(); i != elements_.end(); ++i)
14 std::cout << *i << ’\n’;
15 }
16 private:
17 std::vector<int> elements_;
18 };
19
20 int main() {
21 Sequence seq = {1, 2, 3, 4, 5, 6};
22 seq.print();
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 242
Explicit Constructors
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 243
Example Without Explicit Constructor
1 #include <cstdlib>
2
3 // one-dimensional integer array class
4 class IntArray {
5 public:
6 // create array of int with size elements
7 IntArray(std::size_t size) { /* ... */ };
8 // ...
9 };
10
11 void processArray(const IntArray& x) {
12 // ...
13 }
14
15 int main() {
16 // following lines of code almost certain to be
17 // incorrect, but valid due to implicit type
18 // conversion provided by
19 // IntArray::IntArray(std::size_t)
20 IntArray a = 42;
21 // probably incorrect
22 // implicit conversion effectively yields code:
23 // IntArray a = IntArray(42);
24 processArray(42);
25 // probably incorrect
26 // implicit conversion effectively yields code:
27 // processArray(IntArray(42));
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 244
Example With Explicit Constructor
1 #include <cstdlib>
2
3 // one-dimensional integer array class
4 class IntArray {
5 public:
6 // create array of int with size elements
7 explicit IntArray(std::size_t size) { /* ... */ };
8 // ...
9 };
10
11 void processArray(const IntArray& x) {
12 // ...
13 }
14
15 int main() {
16 IntArray a = 42; // ERROR: cannot convert
17 processArray(42); // ERROR: cannot convert
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 245
Explicitly Deleted/Defaulted Special Member Functions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 246
Delegating Constructors
sometimes, one constructor of class needs to performs all work of another
constructor followed by some additional work
rather than duplicate common code in both constructors, one constructor
can use its initializer list to invoke other constructor (which must be only
one in initializer list)
constructor that invokes another constructor via initializer list called
delegating constructor
example:
class Widget {
public:
Widget(char c, int i) : c_(c), i_(i) {}
Widget(int i) : Widget(’a’, i) {}
// delegating constructor
// ...
private:
char c_;
int i_;
};
int main() {
Widget w(’A’, 42);
Widget v(42);
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 247
Static Data Members
sometimes want to have object that is shared by all objects of class
data member that is shared by all objects of class is called static data
member
to make data member static, declare using static qualifier
static data member must (in most cases) be defined outside body of class
example:
class Widget {
public:
Widget() {++count_;}
Widget(const Widget&) {++count_;}
Widget(Widget&&) {++count_;}
˜Widget() {--count_;}
// ...
private:
static int count_; // total number of Widget
// objects in existence
};
// Define (and initialize) count member.
int Widget::count_ = 0;
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 248
Static Member Functions
sometimes want to have member function that does not operate on
objects of class
member function of class that does not operate on object of class (i.e.,
has no this variable) called static member function
to make member function static, declare using static qualifier
example:
class Widget {
public:
// ...
// convert degrees to radians
static double degToRad(double deg)
{return (M_PI / 180.0) * deg;}
private:
// ...
};
void func() {
Widget x; double rad;
rad = Widget::degToRad(45.0);
rad = x.degToRad(45.0); // x is ignored
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 249
constexpr Member Functions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 250
constexpr Constructors
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 251
Example: Constexpr Constructors and Member Functions
1 #include <cmath>
2 #include <iostream>
3
4 // Two-dimensional vector class.
5 class Vector {
6 public:
7 constexpr Vector() : x_(0), y_(0) {}
8 constexpr Vector(double x, double y) : x_(x), y_(y) {}
9 constexpr Vector(const Vector& v) : x_(v.x_), y_(v.y_) {}
10 constexpr Vector& operator=(const Vector& v)
11 {x_ = v.x_; y_ = v.y_; return *this;}
12 constexpr double x() const {return x_;}
13 constexpr double y() const {return y_;}
14 constexpr double norm() const
15 {return std::sqrt(x_ * x_ + y_ * y_);}
16 // ...
17 private:
18 double x_; // The x component of the vector.
19 double y_; // The y component of the vector.
20 };
21
22 int main() {
23 constexpr Vector v(3.0, 4.0);
24 static_assert(v.x() == 3.0 && v.y() == 4.0);
25 constexpr double d = v.norm();
26 std::cout << d << ’\n’;
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 252
Why Constexpr Member Functions Not Implicitly Const
class Widget {
public:
constexpr Widget() : i_(42) {}
constexpr const int& get() const {return i_;}
constexpr int& get() /* what if implicitly const? */
{return i_;}
// ...
private:
int i_;
};
constexpr int i = ++Widget().get();
static_assert(i == 43);
in above code example, we want to have const and non-const overloads of get
member function that can each be used in constant expressions
so both overloads of get need to be constexpr
if constexpr member functions were implicitly const, it would be impossible to
overload on const in manner we wish to do here, since second overload of get
would automatically become const member function (resulting in multiple
conflicting definitions of const member function get)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 253
The mutable Qualifier
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 254
Example: Mutable Qualifier for Statistical Information
1 #include <iostream>
2 #include <string>
3
4 class Employee {
5 public:
6 Employee(int id, std::string& name, double salary) :
7 id_(id), name_(name), salary_(salary), accessCount_(0) {}
8 int getId() const {
9 ++accessCount_; return id_;
10 }
11 std::string getName() const {
12 ++accessCount_; return name_;
13 }
14 double getSalary() const {
15 ++accessCount_; return salary_;
16 }
17 // ...
18 // for debugging
19 void outputDebugInfo(std::ostream& out) const {
20 out << accessCount_ << ’\n’;
21 }
22 private:
23 int id_; // employee ID
24 std::string name_; // employee name
25 double salary_; // employee salary
26 mutable unsigned long accessCount_; // for debugging
27 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 255
Pointers to Members
inserter and extractor should use compatible formats (i.e., what is written
by inserter should be readable by extractor)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 258
Stream Extractors
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 259
Structured Bindings
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 260
Structured Bindings Example
1 #include <tuple>
2 #include <array>
3 #include <cassert>
4
5 int main() {
6 int a[3] = {1, 2, 3};
7 auto [a0, a1, a2] = a;
8 assert(a0 == a[0] && a1 == a[1] && a2 == a[2]);
9
10 int b[3] = {0, 2, 3};
11 auto& [b0, b1, b2] = b;
12 ++b0;
13 assert(b[0] == 1);
14
15 std::array<int, 3> c = {1, 2, 3};
16 auto [c0, c1, c2] = c;
17 assert(c0 == c[0] && c1 == c[1] && c2 == c[2]);
18
19 auto t = std::make_tuple(true, 42, ’A’);
20 auto [tb, ti, tc] = t;
21 assert(tb == true && ti == 42 && tc == ’A’);
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 261
Structured Bindings Example
1 #include <map>
2 #include <string>
3 #include <iostream>
4
5 int main() {
6 std::map<std::string, int> m = {
7 {"apple", 1},
8 {"banana", 2},
9 {"orange", 3},
10 };
11 for (auto&& [key, value] : m) {
12 std::cout << key << ’ ’ << value << ’\n’;
13 }
14 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 262
Literal Types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 264
Constexpr Function Requirements
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 265
Constexpr Constructor Requirements
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 266
Section 2.4.5
Temporary Objects
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 267
Temporary Objects
A temporary object is an unnamed object introduced by the compiler.
Temporary objects are used during:
evaluation of expressions
argument passing
function returns (that return by value)
reference initialization
It is important to understand when temporary objects can be introduced,
since the introduction of temporaries impacts performance.
Evaluation of expression:
std::string s1("Hello ");
std::string s2("World");
std::string s;
s = s1 + s2; // must create temporary
// std::string _tmp(s1 + s2);
// s = _tmp;
Argument passing:
double func(const double& x);
func(3); // must create temporary
// double _tmp = 3;
// func(_tmp);
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 268
Temporary Objects (Continued)
Reference initialization:
int i = 2;
const double& d = i; // must create temporary
// double _tmp = i;
// const double& d = _tmp;
Function return:
std::string getMessage();
std::string s;
s = getMessage(); // must create temporary
// std::string _tmp(getMessage());
// s = _tmp;
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 269
Temporary Objects Example
1 class Complex {
2 public:
3 Complex(double re = 0.0, double im = 0.0) : re_(re),
4 im_(im) {}
5 Complex(const Complex& a) = default;
6 Complex(Complex&& a) = default;
7 Complex& operator=(const Complex& a) = default;
8 Complex& operator=(Complex&& a) = default;
9 ˜Complex() = default;
10 double real() const {return re_;}
11 double imag() const {return im_;}
12 private:
13 double re_; // The real part.
14 double im_; // The imaginary part.
15 };
16
17 Complex operator+(const Complex& a, const Complex& b) {
18 return Complex(a.real() + b.real(), a.imag() + b.imag());
19 }
20
21 int main() {
22 Complex a(1.0, 2.0);
23 Complex b(1.0, 1.0);
24 Complex c;
25 // ...
26 c = a + b;
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 270
Temporary Objects Example (Continued)
Original code:
int main() {
Complex a(1.0, 2.0);
Complex b(1.0, 1.0);
Complex c;
// ...
c = a + b;
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 271
Prefix Versus Postfix Increment/Decrement
1 class Counter {
2 public:
3 Counter() : count_(0) {}
4 int getCount() const {return count_;}
5 Counter& operator++() { // prefix increment
6 ++count_;
7 return *this;
8 }
9 Counter operator++(int) { // postfix increment
10 Counter old(*this);
11 ++count_;
12 return old;
13 }
14 private:
15 int count_; // counter value
16 };
17
18 int main() {
19 Counter x;
20 Counter y;
21 y = ++x; // no temporaries, int increment, operator=
22 y = x++; // 1 temporary, 1 named, 2 constructors,
23 // 2 destructors, int increment, operator=
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 272
Compound Assignment Versus Separate Assignment
1 #include <complex>
2 using std::complex;
3
4 int main() {
5 complex<double> a(1.0, 1.0);
6 complex<double> b(1.0, -1.0);
7 complex<double> z(0.0, 0.0);
8
9 // 2 temporary objects
10 // 2 constructors, 2 destructors
11 // 1 operator=, 1 operator+, 1 operator*
12 z = b * (z + a);
13
14 // no temporary objects
15 // only 1 operator+= and 1 operator*=
16 z += a;
17 z *= b;
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 273
Lifetime of Temporary Objects
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 274
Lifetime of Temporary Objects Examples
Example:
void func() {
std::string s1("Hello");
std::string s2(" ");
std::string s3("World!\n");
const std::string& s = s1 + s2 + s3;
std::cout << s; // OK?
}
Example:
const std::string& getString() {
return std::string("Hello");
}
void func() {
std::cout << getString(); // OK?
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 275
Return Value Optimization (RVO)
return value optimization (RVO) is compiler optimization technique that
eliminates copy of return value from unnamed local object in function to object in
caller
example:
SomeType function() {
return SomeType(); // returns temporary object
}
void caller() {
SomeType x = function();
}
without RVO: return value of function (which is local to function) is copied to new
temporary object in caller (so return value not lost when function returns); then,
value of new temporary object copied to object that is to hold return value
with RVO: return value of function is placed directly in object (in caller) that is to
hold return value
by avoiding need for temporary object to hold return value, eliminates move/copy
constructor and destructor call
as will be seen later, C++ requires this type of optimization to be performed
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 276
Named Return Value Optimization (NRVO)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 277
Copy Elision
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 278
Mandatory Copy Elision
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 279
Copy Elision and Returning by Value
in return statement of function with class return type, when expression is name of
non-volatile automatic object (other than function or catch-clause parameter) with
same cv-unqualified type as function return type, automatic object can be
constructed directly in function’s return value
example:
1 #include <iostream>
2
3 class Widget {
4 public:
5 Widget() {}
6 Widget(const Widget&) {std::cout << "copy\n";}
7 Widget(Widget&&) {std::cout << "move\n";}
8 // ...
9 };
10
11 Widget func1() {return Widget();}
12 Widget func2() {Widget w; return w;}
13
14 int main() {
15 Widget w = func1(); // required copy elision
16 Widget x = func2(); // possible copy elision
17 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 280
Copy Elision and Passing by Value
in function call, when temporary class object not bound to reference would be
copied/moved to class object with same cv-unqualified type, temporary object
can be constructed directly in target of omitted copy/move
example:
1 #include <iostream>
2
3 class Widget {
4 public:
5 Widget() : x_(42) {}
6 Widget(const Widget&) {std::cout << "copy\n";}
7 Widget(Widget&&) {std::cout << "move\n";}
8 int get() const {return x_;}
9 // ...
10 private:
11 int x_;
12 };
13
14 void func(Widget w) {std::cout << w.get() << ’\n’;}
15
16 int main() {
17 func(Widget()); // required copy elision
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 281
Copy Elision and Throwing by Value
in throw expression, when operand is name of non-volatile automatic object
(other than function or catch-clause parameter) whose scope does not extend
beyond end of innermost enclosing try block (if there is one), copy/move
operation from operand to exception object can be omitted by constructing
automatic object directly into exception object
example:
1 #include <iostream>
2
3 class Widget {
4 public:
5 Widget() {}
6 Widget(const Widget &) {std::cout << "copy\n";}
7 Widget(Widget&&) {std::cout << "move\n";}
8 // ...
9 };
10
11 void f(){
12 throw Widget(); // required copy elision
13 }
14
15 int main() {
16 try {f();}
17 catch (Widget foo) {std::cout << "catch\n";}
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 282
Copy Elision and Catching by Value
when exception declaration of exception handler declares object of same type
(except for cv-qualification) as exception object, copy/move operation can be
omitted by treating exception declaration as alias for exception object if meaning
of program will be unchanged except for execution of constructors and
destructors for object declared by exception declaration
example:
1 #include <iostream>
2
3 class Widget {
4 public:
5 Widget() {}
6 Widget(const Widget &) {std::cout << "copy\n";}
7 Widget(Widget&&) {std::cout << "move\n";}
8 // ...
9 };
10
11 void f(){throw Widget();}
12
13 int main() {
14 try {f();}
15 catch (Widget foo) { // possible copy elision
16 std::cout << "catch\n";
17 }
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 283
Mandatory Copy Elision Example: Factory Function
1 class Widget {
2 public:
3 Widget() {/* ... */}
4 // not copyable
5 Widget(const Widget&) = delete;
6 Widget& operator=(const Widget&) = delete;
7 // not movable
8 Widget(Widget&&) = delete;
9 Widget& operator=(Widget&&) = delete;
10 // ...
11 };
12
13 Widget make_widget() {
14 return Widget();
15 }
16
17 int main() {
18 Widget w(make_widget());
19 // OK: copy elision required
20 Widget v(Widget());
21 // OK: copy elision required
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 284
Mandatory Copy Elision Example: Constant Expressions
1 #include <iostream>
2
3 struct Widget {
4 Widget *p;
5 constexpr Widget() : p(this) {}
6 };
7
8 constexpr Widget func() {
9 Widget w;
10 return w; // NOTE: returning named object
11 }
12
13 constexpr Widget a;
14 static_assert(a.p == &a);
15
16 constexpr Widget b = func();
17 static_assert(b.p == &b);
18 // OK: required copy elision (NVRO guaranteed here)
19
20 int main() {
21 Widget c = func();
22 // c.p may point to c or to a temporary
23 std::cout << (c.p == &c) << ’\n’;
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 285
Section 2.4.6
Functors
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 286
Functors
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 287
Functor Example: Less Than
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 288
Functor Example With State
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 289
Section 2.5
Templates
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 290
Templates
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 291
Section 2.5.1
Function Templates
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 292
Motivation for Function Templates
each of above functions has same general form; that is, for some type T,
we have:
T max(T x, T y)
{return x > y ? x : y;}
would be nice if we did not have to repeatedly type, debug, test, and
maintain nearly identical code
in effect, would like code to be parameterized on type T
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 293
Function Templates
function template is family of functions parameterized by one or
parameters
each template parameter can be: non-type (e.g., integral constant), type,
template, or parameter pack (in case of variadic template)
syntax for template function has general form:
template <parameter list> function declaration
parameter list: parameters on which template function depends
function declaration: function declaration or definition
type parameter designated by class or typename keyword
template parameter designated by template keyword
template template parameter must use class keyword
non-type parameter designed by its type (e.g., bool, int)
example:
// declaration of function template
template <class T> T max(T x, T y);
// definition of function template
template <class T> T max(T x, T y)
{return x > y ? x : y;}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 294
Function Templates (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 295
Function Template Examples
1 // compute minimum of two values
2 template <class T>
3 T min(T x, T y) {
4 return x < y ? x : y;
5 }
6
7 // compute square of value
8 template <typename T>
9 T sqr(T x) {
10 return x * x;
11 }
12
13 // swap two values
14 template <class T>
15 void swap(T& x, T& y) {
16 T tmp = x;
17 x = y;
18 y = tmp;
19 }
20
21 // invoke function/functor multiple times
22 template <int N = 1, typename F, typename T>
23 void invoke(F func, const T& value) {
24 for (int i = 0; i < N; ++i) {
25 func(value);
26 }
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 296
Template Function Overload Resolution
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 297
Qualified Names
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 298
Dependent Names
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 299
Qualified Dependent Names
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 300
Why typename is Needed
1 int x = 42;
2
3 template <class T> void func() {
4 // The compiler must be able to check syntactic
5 // correctness of this template code without
6 // knowing T. Without knowing T, however, the
7 // meaning of following line of code is ambiguous.
8 // Is it a declaration of a variable x or an
9 // expression consisting of a binary operator*
10 // with operands T::foo and x?
11 T::foo* x; // Does T::foo name a type or an object?
12 // ...
13 }
14
15 struct ContainsType {
16 using foo = int; // foo is type
17 // ...
18 };
19
20 struct ContainsValue {
21 static int foo; // foo is value
22 // ...
23 };
24
25 int main() {
26 // Only one of the following lines should be valid.
27 func<ContainsValue>();
28 func<ContainsType>();
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 301
Example: What is wrong with this code?
1 // templates_1_0.cpp
2
3 #include <iostream>
4 #include <complex>
5 #include "templates_1_1.hpp"
6
7 int main() {
8 std::complex a(0.0, 1.0);
9 auto b = square(a);
10 std::cout << b << ’\n’;
11 }
1 // templates_1_1.hpp
2
3 template <class T>
4 T square(const T&);
1 // templates_1_1.cpp
2
3 #include "templates_1_1.hpp"
4
5 template <class T>
6 T square(const T& x) {
7 return x * x;
8 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 302
Section 2.5.2
Class Templates
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 303
Motivation for Class Templates
consider almost identical complex number classes:
1 class ComplexDouble {
2 public:
3 ComplexDouble(double x = 0.0, double y = 0.0) : x_(x), y_(y) {}
4 double real() const { return x_; }
5 double imag() const { return y_; }
6 // ...
7 private:
8 double x_, y_; // real and imaginary parts
9 };
10
11 class ComplexFloat {
12 public:
13 ComplexFloat(float x = 0.0f, float y = 0.0f) : x_(x), y_(y) {}
14 float real() const { return x_; }
15 float imag() const { return y_; }
16 // ...
17 private:
18 float x_, y_; // real and imaginary parts
19 };
again, would be nice if we did not have to repeatedly type, debug, test,
and maintain nearly identical code
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 304
Class Templates
class template is family of classes parameterized on one or more
parameters
each template parameter can be: non-type (e.g., integral constant), type,
template, or parameter pack (in case of variadic template)
syntax has general form:
template <parameter list> class declaration
parameter list: parameter list for class
class declaration: class/struct declaration or definition
example:
// declaration of class template
template <class T, unsigned int size>
class MyArray;
// definition of class template
template <class T, unsigned int size>
class MyArray {
// ...
T array_[size];
};
MyArray<double, 100> x;
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 305
Class Templates (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 306
Class Template Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 307
Class-Template Default Parameters
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 308
Qualified Dependent Names
qualified dependent name assumed not to name type, unless preceded by
typename keyword
in following example, note use of typename keyword:
1 #include <vector>
2
3 template <class T> class Vector {
4 public:
5 using Coordinate = typename T::Coordinate;
6 using Distance = typename T::Distance;
7 Vector(const std::vector<Coordinate>& coords) :
8 coords_(coords) {}
9 Distance squaredLength() const {
10 Distance d = Distance(0);
11 for (typename
12 std::vector<Coordinate>::const_iterator i =
13 coords_.begin(); i != coords_.end(); ++i) {
14 typename std::vector<Coordinate>::value_type
15 x = *i;
16 d += x * x;
17 }
18 return d;
19 }
20 // ...
21 private:
22 std::vector<Coordinate> coords_;
23 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 309
Template Template Parameter Example
1 #include <vector>
2 #include <list>
3 #include <deque>
4 #include <memory>
5
6 template <template <class, class> class Container, class Value>
7 class Stack {
8 public:
9 // ...
10 private:
11 Container<Value, std::allocator<Value>> data_;
12 };
13
14 int main() {
15 Stack<std::vector, int> s1;
16 Stack<std::list, int> s2;
17 Stack<std::deque, int> s3;
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 310
Class Template Parameter Deduction
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 311
Class Template Parameter Deduction Example
1 #include <vector>
2 #include <tuple>
3 #include <set>
4 #include <string>
5
6 using namespace std::string_literals;
7
8 auto get_tuple() {
9 return std::tuple("Zaphod"s, 42);
10 // deduces tuple<std::string, int>
11 }
12
13 int main() {
14 std::vector v{1, 2, 3};
15 // deduces vector<int>
16 std::tuple t(true, ’A’, 42);
17 // deduces tuple<bool, char, int>
18 std::pair p(42, "Hello"s);
19 // deduces pair<int, std::string>
20 std::set s{0.5, 0.25};
21 // deduces set<double>
22 //auto ptr = new std::tuple(true, 42);
23 // should deduce tuple<bool, int>?
24 // fails to compile with GCC 7.1.0
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 312
Template Deduction Guides
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 313
Template Deduction Guide Example
1 #include <string>
2 #include <type_traits>
3
4 using namespace std::string_literals;
5
6 template <class T>
7 class Name {
8 public:
9 Name(T first, T last) : first_(first), last_(last) {}
10 // ...
11 private:
12 T first_;
13 T last_;
14 };
15
16 // deduction guide
17 Name(const char*, const char*) -> Name<std::string>;
18
19 int main() {
20 Name n("Zaphod", "Beeblebrox");
21 // deduces Name<std::string> via deduction guide
22 static_assert(std::is_same_v<decltype(n), Name<std::string>>);
23 Name n2("Jane"s, "Doe"s);
24 // deduces Name<std::string> (without deduction guide)
25 static_assert(std::is_same_v<decltype(n2), Name<std::string>>);
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 314
Auto Non-Type Template Parameters
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 315
Example Without Auto Non-Type Template Parameter
1 #include <cstdlib>
2 #include <iostream>
3
4 template<class T, T v>
5 struct integral_constant {
6 using value_type = T;
7 static constexpr value_type value = v;
8 using type = integral_constant;
9 constexpr operator value_type() const noexcept
10 {return value;}
11 constexpr value_type operator()() const noexcept
12 {return value;}
13 };
14
15 using forty_two_type = integral_constant<int, 42>;
16
17 int main() {
18 constexpr forty_two_type x;
19 constexpr auto v = x.value;
20 std::cout << v << ’\n’;
21 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 316
Example With Auto Non-Type Template Parameter
1 #include <cstdlib>
2 #include <iostream>
3
4 template<auto v>
5 struct integral_constant {
6 using value_type = decltype(v);
7 static constexpr value_type value = v;
8 using type = integral_constant;
9 constexpr operator value_type() const noexcept
10 {return value;}
11 constexpr value_type operator()() const noexcept
12 {return value;}
13 };
14
15 using forty_two_type = integral_constant<42>;
16
17 int main() {
18 constexpr forty_two_type x;
19 constexpr auto v = x.value;
20 std::cout << v << ’\n’;
21 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 317
Section 2.5.3
Variable Templates
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 318
Variable Templates
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 319
Variable Template Example: pi
1 #include <limits>
2 #include <complex>
3 #include <iostream>
4
5 template <typename T>
6 constexpr T pi =
7 T(3.14159265358979323846264338327950288419716939937510L);
8
9 int main() {
10 std::cout.precision(
11 std::numeric_limits<long double>::max_digits10);
12 std::cout
13 << pi<int> << ’\n’
14 << pi<float> << ’\n’
15 << pi<double> << ’\n’
16 << pi<long double> << ’\n’
17 << pi<std::complex<float>> << ’\n’
18 << pi<std::complex<double>> << ’\n’
19 << pi<std::complex<long double>> << ’\n’;
20 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 320
Section 2.5.4
Alias Templates
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 321
Alias Templates
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 322
Alias Template Example
1 #include <iostream>
2 #include <set>
3
4 // alias template for set that employs std::greater for
5 // comparison
6 template <typename Value,
7 typename Alloc = std::allocator<Value>>
8 using GreaterSet = std::set<Value,
9 std::greater<Value>, Alloc>;
10
11 int main() {
12 std::set x{1, 4, 3, 2};
13 GreaterSet<int> y{1, 4, 3, 2};
14 for (auto i : x) {
15 std::cout << i << ’\n’;
16 }
17 std::cout << ’\n’;
18 for (auto i : y) {
19 std::cout << i << ’\n’;
20 }
21 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 323
Section 2.5.5
Variadic Templates
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 324
Variadic Templates
language provides ability to specify template that can take variable
number of arguments
template that can take variable number of arguments called variadic
template
alias templates, class templates, function templates, and variable
templates may be variadic
variable number of arguments specified by using what is called parameter
pack
parameter pack is parameter that accepts (i.e., is placeholder for) zero or
more arguments (of same kind)
parameter pack used in parameter list of template to allow to variable
number of template parameters
ellipsis (i.e., “...”) is used in various contexts relating to parameter packs
ellipsis after designator for kind of template argument in template
parameter list designates argument is parameter pack
ellipsis after parameter pack parameter expands parameter pack in
context-sensitive manner
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 325
Parameter Packs
syntax for non-type template parameter pack named Args and containing
elements of type type (e.g., bool, int, unsigned int):
type... Args
example:
template <int... Is> /* ... */
Is is (non-type) template parameter pack that corresponds to zero or
more (compile-time constant) values of type int
syntax for type template parameter pack named Args:
typename... Args
or equivalently
class... Args
examples:
template <typename... Ts> /* ... */
template <class... Ts> /* ... */
Ts is (type) template parameter pack that corresponds to zero or more
types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 326
Parameter Packs (Continued 1)
syntax for template template parameter pack named Args:
template <parameter list> typename... Args
or equivalently
template <parameter list> class... Args
example:
template <template <class T> class... Ts>
/* ... */
Ts is (template) template parameter pack that corresponds to zero or
more templates
syntax for function parameter pack named args whose elements have
types corresponding to elements of type template parameter pack Args:
Args... args
example:
template <class... Ts> void func(Ts... args);
args is (function) parameter pack that correponds to zero or more
function parameters whose types correspond to elements of type
parameter pack Ts
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 327
Parameter Packs (Continued 2)
in context where template arguments cannot be deduced (e.g., primary
class templates), only last template parameter can be parameter pack
in context where template arguments can be deduced (e.g., function
templates and class template partial specializations), template parameter
pack need not be last template parameter
example:
1 template <class U, class... Ts> class C1 { /* ... */ };
2 // OK: Ts is last template parameter
3
4 template <class... Ts, class U> class C2 { /* ... */ };
5 // ERROR: Ts not last and U not deduced
6
7 template <class... Ts, class U> void f1(Ts... ts)
8 { /* ... */ } // NOT OK: Ts not last and U not deduced
9
10 template <class... Ts, class U> void f2(Ts... ts, U u)
11 { /* ... */ } // OK: Ts not last but U is deduced
12
13 int main() {
14 f1<int, int, bool>(1, 2, true);
15 // ERROR: no matching function call
16 f2<int, int>(1, 2, true); // OK
17 f2(1, 2, true); // ERROR: one argument expected
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 328
Parameter Pack Expansion
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 329
Variadic Template Examples
1 #include <tuple>
2
3 // variadic alias template
4 template <class... T>
5 using My_tuple = std::tuple<bool, T...>;
6
7 // variadic class template
8 template <int... Values>
9 class Integer_sequence {
10 // ...
11 };
12
13 // variadic function template
14 template <class... Ts>
15 void print(const Ts&... values) {
16 // ...
17 }
18
19 // variadic variable template
20 template <typename T, T... Values>
21 constexpr T array[] = {Values...};
22
23 int main() {
24 Integer_sequence<1, 3, 4, 2> x;
25 auto a = array<int, 1, 2, 4, 8>;
26 My_tuple<int, double> t(true, 42, 42.0);
27 print(1’000’000, 1, 43.2, "Hello");
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 330
Parameter Pack Expansion
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 331
The sizeof... Operator
example:
#include <cassert>
template <typename... Ts>
int number_of_arguments(const Ts&... args) {
return sizeof...(args);
}
int main() {
assert(number_of_arguments(1, 2, 3) == 3);
assert(number_of_arguments() == 0);
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 332
Variadic Function Template: sum
1 #include <iostream>
2 #include <string>
3
4 using namespace std::string_literals;
5
6 template <class T>
7 auto sum(T x) {
8 return x;
9 }
10
11 template <class T, class... Args>
12 auto sum(T x, Args... args) {
13 return x + sum(args...);
14 }
15
16 int main() {
17 auto x = sum(42.5, -1.0, 0.5f);
18 auto y = sum("The "s, "answer "s, "is "s);
19 std::cout << y << x << ".\n";
20 // sum(); // ERROR: no matching function call
21 }
22
23 /* Output:
24 The answer is 42.
25 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 333
Variadic Function Template: maximum
1 #include <type_traits>
2 #include <string>
3 #include <cassert>
4
5 using namespace std::string_literals;
6
7 template <typename T>
8 T maximum(const T& a) {return a;}
9
10 template <typename T1, typename T2>
11 typename std::common_type_t<const T1&, const T2&>
12 maximum(const T1 &a, const T2 &b) {
13 return a > b ? a : b;
14 }
15
16 template <typename T1, typename T2, typename... Args>
17 typename std::common_type_t<const T1&, const T2&,
18 const Args&...>
19 maximum(const T1& a, const T2& b, const Args&... args) {
20 return maximum(maximum(a, b), args...);
21 }
22
23 int main() {
24 assert(maximum(1) == 1);
25 assert(maximum(1, 2, 3, 4, -1.4) == 4);
26 assert(maximum(-1’000’000L, -42L, 10, 42.42) == 42.42);
27 assert(maximum("apple"s, "zebra"s, "c++"s) == "zebra"s);
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 334
Variadic Function Template With Template Template
Parameter: print_container
1 #include <iostream>
2 #include <vector>
3 #include <string>
4 #include <set>
5
6 template <template <class, class...>
7 class ContainerType, class ValueType, class... Args>
8 bool print_container(const ContainerType<ValueType, Args...>&
9 c) {
10 for (auto i = c.begin(); i != c.end();) {
11 std::cout << *i;
12 if (++i != c.end()) {std::cout << ’ ’;}
13 }
14 std::cout << ’\n’;
15 return bool(std::cout);
16 }
17
18 int main() {
19 using namespace std::string_literals;
20 std::vector vi{1, 2, 3, 4, 5};
21 std::set si{5, 4, 3, 2, 1};
22 std::set ss{"world"s, "hello"s};
23 print_container(vi);
24 print_container(si);
25 print_container(ss);
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 335
Variadic Class Template: Integer_sequence
1 #include <iostream>
2 #include <cstdlib>
3
4 template <class T, T... Values>
5 class Integer_sequence {
6 public:
7 using value_type = T;
8 using const_iterator = const T*;
9 constexpr std::size_t size() const
10 {return sizeof...(Values);}
11 constexpr T operator[](int i) const {return values_[i];}
12 constexpr const_iterator begin() const
13 {return &values_[0];}
14 constexpr const_iterator end() const
15 {return &values_[size()];}
16 private:
17 static constexpr T values_[sizeof...(Values)] =
18 {Values...};
19 };
20
21 template <class T, T... Values>
22 constexpr T
23 Integer_sequence<T, Values...>::values_[sizeof...(Values)];
24
25 int main() {
26 Integer_sequence<std::size_t, 1, 2, 4, 8> seq;
27 std::cout << seq.size() << ’\n’ << seq[0] << ’\n’;
28 for (auto i : seq) {std::cout << i << ’\n’;}
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 336
Variadic Variable Template: int_array
1 #include <iostream>
2
3 template <int... Args>
4 constexpr int int_array[] = {Args...};
5
6 int main() {
7 for (auto i : int_array<1,2,4,8>) {
8 std::cout << i << ’\n’;
9 }
10 }
11
12 /* Output:
13 1
14 2
15 4
16 8
17 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 337
Variadic Alias Template: My_tuple
1 #include <iostream>
2 #include <string>
3 #include <tuple>
4
5 template <class... Ts>
6 using My_tuple = std::tuple<bool, Ts...>;
7
8 int main() {
9 My_tuple<int, std::string> t(true, 42,
10 "meaning of life");
11 std::cout << std::get<0>(t) << ’ ’
12 << std::get<1>(t) << ’ ’
13 << std::get<2>(t) << ’\n’;
14 }
15
16 /* Output:
17 1 42 meaning of life
18 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 338
Fold Expressions
may want to apply binary operator (such as +) across all elements in
parameter pack
fold expression reduces (i.e., folds) parameter pack over binary operator
op: binary operator
E : expression that contains unexpanded parameter pack
I : expression that does not contain unexpanded parameter pack
Fold Syntax Expansion
unary left (... op E) ((E1 op E2 ) op ...) op EN
unary right (E op . . . ) E1 op (... op (EN−1 op EN ))
binary left (I op ... op E) (((I op E1 ) op E2 ) op ...) op EN
binary right (E op ... op I) E1 op (... op (EN−1 op (EN op I)))
unary fold of empty parameter pack:
Operator Value for Empty Parameter Pack
&& true
|| false
, void()
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 339
Sum Example Without Fold Expression
1 #include <iostream>
2 #include <string>
3
4 using namespace std::string_literals;
5
6 template <class T>
7 auto sum(T x) {
8 return x;
9 }
10
11 template <class T, class... Args>
12 auto sum(T x, Args... args) {
13 return x + sum(args...);
14 }
15
16 int main() {
17 auto x = sum(42.5, -1.0, 0.5f);
18 auto y = sum("The "s, "answer "s, "is "s);
19 std::cout << y << x << ".\n";
20 // sum(); // ERROR: no matching function call
21 }
22
23 /* Output:
24 The answer is 42.
25 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 340
Sum Example With Fold Expression
1 #include <iostream>
2 #include <string>
3
4 using namespace std::string_literals;
5
6 template <class T, class... Args>
7 auto sum(T x, Args... args) {
8 return x + (... + args);
9 }
10
11 int main() {
12 auto x = sum(42.5, -1.0, 0.5f);
13 auto y = sum("The "s, "answer "s, "is "s);
14 std::cout << y << x << ".\n";
15 // sum(); // ERROR: no matching function call
16 }
17
18 /* Output:
19 The answer is 42.
20 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 341
Print Example Without Fold Expression
1 #include <iostream>
2 #include <string>
3
4 using namespace std::string_literals;
5
6 std::ostream& print() {return std::cout;}
7
8 template <class T>
9 std::ostream& print(const T& value) {
10 return std::cout << value;
11 }
12
13 template <class T, class... Args>
14 std::ostream& print(const T& value, const Args&... args) {
15 if (!(std::cout << value)) {
16 return std::cout;
17 }
18 return print(args...);
19 }
20
21 int main() {
22 print("The "s, "answer "s, "is "s, 42, ".\n"s);
23 print(); // OK: no-op
24 }
25
26 /* Output:
27 The answer is 42.
28 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 342
Print Example With Fold Expression
1 #include <iostream>
2 #include <string>
3
4 using namespace std::string_literals;
5
6 template <class... Args>
7 std::ostream& print(const Args&... args) {
8 return (std::cout << ... << args);
9 }
10
11 int main() {
12 print("The "s, "answer "s, "is "s, 42, ".\n"s);
13 print(); // OK: no-op
14 }
15
16 /* Output:
17 The answer is 42.
18 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 343
Fold Expression Example: All/Any/One/Even
1 #include <cassert>
2
3 template <class... Args>
4 bool all(Args... args)
5 {return (... && args);}
6
7 template <class... Args>
8 bool any(Args... args)
9 {return (... || args);}
10
11 template <class... Args>
12 bool one(Args... args)
13 {return (0 + ... + args) == 1;}
14
15 template <class... Args>
16 bool even(Args... args)
17 {return (1 + ... + args) % 2;}
18
19 int main() {
20 assert(all(false, true, true) == false);
21 assert(all(true, true, true) == true);
22 assert(any(false, false, true) == true);
23 assert(any(false, false, false) == false);
24 assert(one(true, false, false) == true);
25 assert(one(true, true, false) == false);
26 assert(even(true, true, false) == true);
27 assert(even(true, false, false) == false);
28 assert(even() == true && one() == false);
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 344
Constexpr-Friendly Heterogeneous List Example
1 #include <iostream>
2 #include <tuple>
3
4 // heterogeneous list of constant values
5 template <auto... vs> class value_list {
6 public:
7 constexpr value_list() : v_(vs...) {}
8 template <int n> constexpr auto get() const
9 {return std::get<n>(v_);}
10 constexpr int size() const {return sizeof...(vs);}
11 private:
12 std::tuple<decltype(vs)...> v_;
13 };
14
15 int main() {
16 constexpr value_list<42, true, ’A’> v;
17 constexpr auto n = v.size();
18 constexpr auto a = v.get<0>();
19 constexpr auto b = v.get<1>();
20 constexpr auto c = v.get<2>();
21 std::cout << n << ’ ’ << a << ’ ’ << b << ’ ’ << c << ’\n’;
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 345
Constexpr-Friendly Homogeneous List Example
1 #include <iostream>
2 #include <tuple>
3
4 // homogeneous list of constant values
5 template <auto v1, decltype(v1)... vs> class value_list {
6 public:
7 constexpr value_list() : v_(v1, vs...) {}
8 template <int n> constexpr auto get() const
9 {return std::get<n>(v_);}
10 constexpr int size() const {return 1 + sizeof...(vs);}
11 private:
12 std::tuple<decltype(v1), decltype(vs)...> v_;
13 };
14
15 int main() {
16 constexpr value_list<1, 2, 3> v;
17 constexpr auto n = v.size();
18 constexpr auto a = v.get<0>();
19 constexpr auto b = v.get<1>();
20 constexpr auto c = v.get<2>();
21 std::cout << n << ’ ’ << a << ’ ’ << b << ’ ’ << c << ’\n’;
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 346
Section 2.5.6
Template Specialization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 347
Template Specialization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 349
Partial Specialization
syntax for partial specialization of class template:
template <parameter list> class key
class name <argument list> declaration
syntax for partial specialization of variable template:
template <parameter list> type name
variable name <argument list> declaration
class key: class or struct keyword (for class template)
class name: class being specialized (for class template)
type name: type of variable (for variable template)
variable name: variable being specialized (for variable template)
argument list: template argument list
declaration: declaration of templated entity (e.g., class, variable)
example:
// unspecialized template
template <class T, int N> class Widget { /* ... */ };
// partial specialization of template
// (for when first template parameter is bool)
template <int N> class Widget<bool, N> { /* ... */ };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 350
Explicitly-Specialized Function Template: printPointee
1 #include <iostream>
2
3 // unspecialized version
4 template <class T>
5 typename std::ostream& printPointee(
6 typename std::ostream& out, const T* p)
7 {return out << *p << ’\n’;}
8
9 // specialization
10 template <>
11 typename std::ostream& printPointee<void>(
12 typename std::ostream& out, const void* p)
13 {return out << *static_cast<const char*>(p) << ’\n’;}
14
15 int main() {
16 int i = 42;
17 const int* ip = &i;
18 char c = ’A’;
19 const void* vp = &c;
20 printPointee(std::cout, ip);
21 printPointee(std::cout, vp);
22 }
23
24 /* Output:
25 42
26 A
27 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 351
Explicitly-Specialized Class Template: is_void
1 template <class T>
2 struct is_void
3 {static constexpr bool value = false;};
4
5 template <>
6 struct is_void<void>
7 {static constexpr bool value = true;};
8
9 template <>
10 struct is_void<const void>
11 {static constexpr bool value = true;};
12
13 template <>
14 struct is_void<volatile void>
15 {static constexpr bool value = true;};
16
17 template <>
18 struct is_void<const volatile void>
19 {static constexpr bool value = true;};
20
21 static_assert(is_void<int>::value == false, "");
22 static_assert(is_void<double*>::value == false, "");
23 static_assert(is_void<void>::value == true, "");
24 static_assert(is_void<const void>::value == true, "");
25 static_assert(is_void<volatile void>::value == true , "");
26 static_assert(is_void<const volatile void>::value == true,
27 "");
28
29 int main() {}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 352
Partially-Specialized Class Template
1 #include <iostream>
2
3 // unspecialized version
4 template <typename T, typename V>
5 struct Widget {
6 Widget() {std::cout << "unspecialized\n";}
7 };
8
9 // partial specialization
10 template <typename T>
11 struct Widget<int, T> {
12 Widget() {std::cout << "partial\n";}
13 };
14
15 // explicit specialization
16 template <>
17 struct Widget<int, int> {
18 Widget() {std::cout << "explicit\n";}
19 };
20
21 int main() {
22 Widget<double, int> w1; // unspecialized verion
23 Widget<int, double> w2; // partial specialization
24 Widget<int, int> w3; // explicit specialization
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 353
Partially-Specialized Class Template: std::vector
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 354
Explicitly-Specialized Variable Template: is_void_v
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 355
Explicitly-Specialized Variable Template: factorial
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 356
Partially-Specialized Variable Template: quotient
1 #include <limits>
2
3 // unspecialized version
4 template <int X, int Y>
5 constexpr int quotient = X / Y;
6
7 // partial specialization (which prevents division by zero)
8 template <int X>
9 constexpr int quotient<X, 0> = (X < 0) ?
10 std::numeric_limits<int>::min() :
11 std::numeric_limits<int>::max();
12
13 static_assert(quotient<4, 2> == 2, "");
14 static_assert(quotient<5, 3> == 1, "");
15 static_assert(quotient<4, 0> ==
16 std::numeric_limits<int>::max(), "");
17 static_assert(quotient<-4, 0> ==
18 std::numeric_limits<int>::min(), "");
19
20 int main() {}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 357
Section 2.5.7
Miscellany
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 358
Overload Resolution and Substitution Failure
when creating candidate set (of functions) for overload resolution, some or
all candidates of that set may be result of instantiated templates with
template arguments substituted for corresponding template parameters
process of substituting template arguments for corresponding template
parameters can lead to invalid code
if certain types of invalid code result from substitution in any of following,
substitution failure said to occur:
all types used in function type (i.e., return type and types of all parameters)
all types used in template parameter declarations
all expressions used in function type
all expressions used in template parameter declaration
substitution failure not treated as error
instead, substitution failure simply causes overload to be removed from
candidate set
this behavior often referred to by term “substitution failure is not an error
(SFINAE)”
SFINAE behavior often exploited in template metaprogramming
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 359
Some Kinds of Substitution Failures
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 360
Some Kinds of Substitution Failures (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 361
SFINAE Example: Truncate
1 class Real {
2 public:
3 using rounded_type = long long;
4 rounded_type truncate() const {
5 rounded_type result;
6 // ...
7 return result;
8 }
9 // ...
10 };
11
12 // function 1
13 template <class T>
14 typename T::rounded_type truncate(const T& x) {return x.truncate();}
15 // NOTE: example would not compile if return type specified as auto
16
17 // function 2
18 int truncate(double x) {return x;}
19
20 int main() {
21 Real r;
22 float f = 3.14f;
23 auto rounded_r = truncate(r);
24 // calls function 1 (only trivial conversions)
25 auto rounded_f = truncate(f);
26 // function 2 requires nontrivial conversions
27 // function 1 would only require trivial conversions but
28 // substitution failure occurs
29 // calls function 2 (with conversions)
30 }
[link: overload resolution]
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 362
SFINAE Example: Truncate Revisited
1 class Real {
2 public:
3 using rounded_type = long long;
4 rounded_type truncate() const {
5 rounded_type result;
6 // ...
7 return result;
8 }
9 // ...
10 };
11
12 // function 1
13 template <class T, class = typename T::rounded_type>
14 auto truncate(const T& x) {return x.truncate();}
15
16 // function 2
17 int truncate(double x) {return x;}
18
19 int main() {
20 Real r;
21 float f = 3.14f;
22 auto rounded_r = truncate(r);
23 // calls function 1 (only trivial conversions)
24 auto rounded_f = truncate(f);
25 // function 2 requires nontrivial conversions
26 // function 1 would only require trivial conversions but
27 // substitution failure occurs
28 // calls function 2 (with conversions)
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 363
std::enable_if and std::enable_if_t
to make SFINAE more convenient to exploit, class template
std::enable_if and alias template std::enable_if_t are provided
declaration of class template enable_if:
template <bool B, class T = void>
struct enable_if;
if B is true, class has member type type defined as T; otherwise, class
has no type member
possible implementation of enable_if:
1 template <bool B, class T = void>
2 struct enable_if {};
3
4 template <class T>
5 struct enable_if<true, T> {
6 using type = T;
7 };
declaration of alias template enable_if_t:
template <bool B, class T = void>
using enable_if_t = typename enable_if<B, T>::type;
if enable_if_t is used with its first parameter as false, substitution
failure will result
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 364
SFINAE Example: Modulo
1 #include <type_traits>
2 #include <cassert>
3 #include <iostream>
4
5 // ISO-Pascal modulo operator for signed integral types
6 template <class T> inline
7 std::enable_if_t<std::is_integral_v<T> && std::is_signed_v<T>, T>
8 mod(T x, T y) {
9 assert(y > 0);
10 if (x < 0) {x += (((-x) / y) + 1) * y;}
11 return x % y;
12 }
13
14 // ISO-Pascal modulo operator for unsigned integral types
15 template <class T> inline
16 std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, T>
17 mod(T x, T y)
18 {return x % y;}
19
20 int main() {
21 auto si = mod(-4, 3); // uses signed version
22 auto ui = mod(5u, 3u); // uses unsigned version
23 auto slli = mod(-5ll, 3ll); // uses signed version
24 auto ulli = mod(4ull, 3ull); // uses unsigned version
25 // auto f = mod(3.0, 4.0);
26 // ERROR: no matching function call
27 std::cout << si << ’ ’ << ui << ’ ’ << slli << ’ ’ << ulli << ’\n’;
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 365
Detection Idiom Example
1 #include <iostream>
2 #include <experimental/type_traits>
3
4 class Widget {
5 public:
6 void foo() const {}
7 // ...
8 };
9
10 class Gadget {
11 public:
12 void foo() {}
13 // ...
14 };
15
16 // helper template for testing if class has member function called
17 // foo that can be invoked on const object with no arguments.
18 template <class T>
19 using has_usable_foo_t = decltype(std::declval<const T&>().foo());
20
21 int main() {
22 std::cout
23 << "Widget "
24 << std::experimental::is_detected_v<has_usable_foo_t, Widget>
25 << ’\n’
26 << "Gadget "
27 << std::experimental::is_detected_v<has_usable_foo_t, Gadget>
28 << ’\n’;
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 366
Section 2.5.8
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 367
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 368
Talks I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 369
Talks II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 370
Section 2.6
Lambda Expressions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 371
Motivation for Lambda Expressions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 372
Lambda Expressions
lambda expression consists of:
1 introducer: capture list in square brackets
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 374
Hello World Program Revisited
1 #include <iostream>
2
3 int main() {
4 []{std::cout << "Hello, World!\n";}();
5 }
1 #include <iostream>
2
3 struct Hello {
4 void operator()() const {
5 std::cout << "Hello, World!\n";
6 }
7 };
8
9 int main() {
10 Hello hello;
11 hello();
12 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 375
Comparison Functor Example
1 #include <iostream>
2 #include <algorithm>
3 #include <cstdlib>
4 #include <vector>
5
6 int main() {
7 std::vector<int> v{-3, 3, 4, 0, -2, -1, 2, 1, -4};
8 std::sort(v.begin(), v.end(),
9 [](int x, int y) {return std::abs(x) < std::abs(y);});
10 for (auto x : v) std::cout << x << ’\n’;
11 }
1 #include <iostream>
2 #include <algorithm>
3 #include <cstdlib>
4 #include <vector>
5
6 struct abs_less {
7 bool operator()(int x, int y) const
8 {return std::abs(x) < std::abs(y);}
9 };
10
11 int main() {
12 std::vector<int> v{-3, 3, 4, 0, -2, -1, 2, 1, -4};
13 std::sort(v.begin(), v.end(), abs_less());
14 for (auto x : v) std::cout << x << ’\n’;
15 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 376
Capturing Objects
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 377
std::transform
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 378
Modulus Example
1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4
5 int main() {
6 int m = 2;
7 std::vector<int> v{0, 1, 2, 3};
8 std::transform(v.begin(), v.end(), v.begin(),
9 [m](int x){return x % m;});
10 for (auto x : v) std::cout << x << ’\n’;
11 }
1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4
5 class mod {
6 public:
7 mod(int m_) : m(m_) {}
8 int operator()(int x) const {return x % m;}
9 private:
10 int m;
11 };
12
13 int main() {
14 int m = 2;
15 std::vector<int> v{0, 1, 2, 3};
16 std::transform(v.begin(), v.end(), v.begin(), mod(m));
17 for (auto x : v) std::cout << x << ’\n’;
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 379
Modulus Example: Without Lambda Expression
1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4
5 class mod {
6 public:
7 mod(int m_) : m(m_) {}
8 int operator()(int x) const {return x % m;}
9 private:
10 int m;
11 };
12
13 int main() {
14 int m = 2;
15 std::vector<int> v{0, 1, 2, 3};
16 std::transform(v.begin(), v.end(), v.begin(), mod(m));
17 for (auto x : v) std::cout << x << ’\n’;
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 380
Modulus Example: With Lambda Expression
1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4
5 int main() {
6 int m = 2;
7 std::vector<int> v{0, 1, 2, 3};
8 std::transform(v.begin(), v.end(), v.begin(),
9 [m](int x){return x % m;});
10 for (auto x : v) std::cout << x << ’\n’;
11 }
m captured by value
approximately 0.5 lines of code to generate functor
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 381
std::for_each
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 382
Product Example
1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4
5 int main() {
6 std::vector<int> v{2, 3, 4};
7 int prod = 1;
8 std::for_each(v.begin(), v.end(),
9 [&prod](int x)->void{prod *= x;});
10 std::cout << prod << ’\n’;
11 }
1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4
5 class cum_prod {
6 public:
7 cum_prod(int& prod_) : prod(prod_) {}
8 void operator()(int x) const {prod *= x;}
9 private:
10 int& prod;
11 };
12
13 int main() {
14 std::vector<int> v{2, 3, 4};
15 int prod = 1;
16 std::for_each(v.begin(), v.end(), cum_prod(prod));
17 std::cout << prod << ’\n’;
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 383
Product Example: Without Lambda Expression
1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4
5 class cum_prod {
6 public:
7 cum_prod(int& prod_) : prod(prod_) {}
8 void operator()(int x) const {prod *= x;}
9 private:
10 int& prod;
11 };
12
13 int main() {
14 std::vector<int> v{2, 3, 4};
15 int prod = 1;
16 std::for_each(v.begin(), v.end(), cum_prod(prod));
17 std::cout << prod << ’\n’;
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 384
Product Example: With Lambda Expression
1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4
5 int main() {
6 std::vector<int> v{2, 3, 4};
7 int prod = 1;
8 std::for_each(v.begin(), v.end(),
9 [&prod](int x)->void{prod *= x;});
10 std::cout << prod << ’\n’;
11 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 385
More Variations on Capture
double a = 2.14;
double b = 3.14;
double c = 42.0;
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 386
Generalized Lambda Capture
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 387
Generalized Lambda Capture Example
1 #include <iostream>
2
3 int main() {
4 int x = 0;
5 int y = 1;
6 auto f = [&count = x, inc = y + 1](){
7 return count += inc;
8 };
9 std::cout << f() << ’ ’;
10 std::cout << f() << ’\n’;
11 }
12
13 // output: 2 4
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 388
Generic Lambda Expressions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 389
Generic Lambda Expression Example [Generic]
1 #include <iostream>
2 #include <complex>
3 #include <string>
4
5 int main() {
6 using namespace std::literals;
7 auto add = [](auto x, auto y) {return x + y;};
8 std::cout << add(1, 2) << ’ ’ << add(1.0, 2.0) << ’ ’
9 << add(1.0, 2.0i) << ’ ’ << add("Jell"s, "o"s) << ’\n’;
10 }
1 #include <iostream>
2 #include <complex>
3 #include <string>
4
5 struct Add {
6 template <class T, class U>
7 auto operator()(T x, U y) {return x + y;};
8 };
9
10 int main() {
11 using namespace std::literals;
12 Add add;
13 std::cout << add(1, 2) << ’ ’ << add(1.0, 2.0) << ’ ’
14 << add(1.0, 2.0i) << ’ ’ << add("Jell"s, "o"s) << ’\n’;
15 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 390
Generic Lambda Expression Example [Convenience]
1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4
5 int main() {
6 std::vector<int> v{0, 1, 2, 3, 4, 5, 6, 7};
7 // sort elements of vector in descending order
8 std::sort(v.begin(), v.end(),
9 [](auto i, auto j) {return i > j;});
10 std::for_each(v.begin(), v.end(),
11 [](auto i) {std::cout << i << ’\n’;});
12 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 391
Dealing With Unnamed Types
fact that closure types unnamed causes complications when need arises
to refer to closure type
helpful language features: auto, decltype
helpful library features: std::function
closures can be stored using auto or std::function
closures that do not capture can be “stored” by assigning to function
pointer
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 392
Using auto, decltype, and std::function
1 #include <iostream>
2 #include <functional>
3
4 std::function<double(double)> linear(double a, double b) {
5 return [=](double x){return a * x + b;};
6 }
7
8 int main() {
9 // type of f is std::function<double(double)>
10 auto f = linear(2.0, -1.0);
11 // g has closure type
12 auto g = [](double x){return 2.0 * x - 1.0;};
13 double (*u)(double) = [](double x){return 2.0 * x - 1.0;};
14 // h has same type as g
15 decltype(g) h = g;
16 for (double x = 0.0; x < 10.0; x += 1.0) {
17 std::cout << x << ’ ’ << f(x) << ’ ’ << g(x) <<
18 ’ ’ << h(x) << (*u)(x) << ’\n’;
19 }
20 }
applying function-call operator to f much slower than in case of g and h
when std::function used, inlining of called function probably not
possible
when functor used directly (via function-call operator) inlining is very likely
prefer auto over std::function for storing closures
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 393
operator() as Non-const Member
1 #include <iostream>
2
3 int main()
4 {
5 int count = 5;
6 // Must use mutable in order to be able to
7 // modify count member.
8 auto get_count = [count]() mutable -> int {
9 return count++;
10 };
11
12 int c;
13 while ((c = get_count()) < 10) {
14 std::cout << c << ’\n’;
15 }
16 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 394
Constexpr Lambdas
1 #include <iostream>
2 #include <array>
3
4 template <typename T>
5 constexpr auto multiply_by(T i) {
6 return [i](auto j) {return i * j;};
7 // OK: lambda is literal type so members
8 // are automatically constexpr
9 }
10
11 int main() {
12 constexpr auto mult_by_2 = multiply_by(2);
13 std::array<int, mult_by_2(8)> a;
14 std::cout << a.size() << ’\n’;
15 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 395
Comparison Functors for Containers
1 #include <iostream>
2 #include <vector>
3 #include <set>
4
5 int main() {
6 // The following two lines are the only important ones:
7 auto cmp = [](int* x, int* y){return *x < *y;};
8 std::set<int*, decltype(cmp)> s(cmp);
9
10 // Just for something to do:
11 // Print the elements of v in sorted order with
12 // duplicates removed.
13 std::vector<int> v = {4, 1, 3, 2, 1, 1, 1, 1};
14 for (auto& x : v) {
15 s.insert(&x);
16 }
17 for (auto x : s) {
18 std::cout << *x << ’\n’;
19 }
20 }
note that s is not default constructed
since closure types not default constructible, following would fail:
std::set<int*, decltype(cmp)> s;
note use of decltype in order to specify type of functor
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 396
What Could Possibly Go Wrong?
1 #include <iostream>
2 #include <vector>
3 #include <functional>
4
5 std::vector<int> vec{2000, 4000, 6000, 8000, 10000};
6 std::function<int(int)> func;
7
8 void do_stuff()
9 {
10 int modulus = 10000;
11 func = [&](int x){return x % modulus;};
12 for (auto x : vec) {
13 std::cout << func(x) << ’\n’;
14 }
15 }
16
17 int main()
18 {
19 do_stuff();
20 for (auto x : vec) {
21 std::cout << func(x) << ’\n’;
22 }
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 397
Dangling References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 398
Section 2.6.1
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 399
Talks I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 400
Section 2.7
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 401
Section 2.7.1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 402
Derived Classes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 404
Person Class
1 #include <string>
2
3 class Person {
4 public:
5 Person(const std::string& family_name,
6 const std::string& given_name) :
7 family_name_(family_name), given_name_(given_name) {}
8 std::string family_name() const {return family_name_;}
9 std::string given_name() const {return given_name_;}
10 std::string full_name() const
11 {return family_name_ + ", " + given_name_;}
12 // ...
13 private:
14 std::string family_name_;
15 std::string given_name_;
16 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 405
Student Class Without Inheritance
1 #include <string>
2
3 class Student {
4 public:
5 Student(const std::string& family_name,
6 const std::string& given_name) :
7 family_name_(family_name), given_name_(given_name) {}
8 // NEW
9 std::string family_name() const {return family_name_;}
10 std::string given_name() const {return given_name_;}
11 std::string full_name() const
12 {return family_name_ + ", " + given_name_;}
13 std::string student_id() {return student_id_;} // NEW
14 private:
15 std::string family_name_;
16 std::string given_name_;
17 std::string student_id_; // NEW
18 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 406
Student Class With Inheritance
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 407
Complete Inheritance Example
1 #include <string>
2
3 class Person {
4 public:
5 Person(const std::string& family_name,
6 const std::string& given_name) :
7 family_name_(family_name), given_name_(given_name) {}
8 std::string family_name() const {return family_name_;}
9 std::string given_name() const {return given_name_;}
10 std::string full_name() const
11 {return family_name_ + ", " + given_name_;}
12 // ... (including virtual destructor)
13 private:
14 std::string family_name_;
15 std::string given_name_;
16 };
17
18 class Student : public Person {
19 public:
20 Student(const std::string& family_name,
21 const std::string& given_name,
22 const std::string& student_id) :
23 Person(family_name, given_name),
24 student_id_(student_id) {}
25 std::string student_id() {return student_id_;}
26 private:
27 std::string student_id_;
28 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 408
Class Hierarchies
B C
D E
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 409
Class Hierarchy Example
class definitions:
class Person { /* ... */ };
class Employee : public Person { /* ... */ };
class Student : public Person { /* ... */ };
class Alumnus : public Person { /* ... */ };
class Faculty : public Employee { /* ... */ };
class Staff : public Employee { /* ... */ };
class Grad : public Student { /* ... */ };
class Undergrad : public Student { /* ... */ };
inheritance diagram:
Person
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 411
Types of Inheritance
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 412
Types of Inheritance (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 413
Inheritance and Member Access Example
1 class Base {
2 public:
3 void f();
4 protected:
5 void g();
6 private:
7 int x;
8 };
9
10 class Derived_1 : public Base {
11 // f is public
12 // g is protected
13 // x is not accessible from Derived_1
14 };
15
16 class Derived_2 : protected Base {
17 // f is protected
18 // g is protected
19 // x is not accessible from Derived_2
20 };
21
22 class Derived_3 : private Base {
23 // f is private
24 // g is private
25 // x is not accessible from Derived_3
26 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 414
Public Inheritance Example
1 class Base {
2 public:
3 void func_1();
4 protected:
5 void func_2();
6 private:
7 int x_;
8 };
9
10 class Derived : public Base {
11 public:
12 void func_3() {
13 func_1(); // OK
14 func_2(); // OK
15 x_ = 0; // ERROR: inaccessible
16 }
17 };
18
19 struct Widget : public Derived {
20 void func_4() { func_2(); } // OK
21 };
22
23 int main() {
24 Derived d;
25 d.func_1(); // OK
26 d.func_2(); // ERROR: inaccessible
27 d.x_ = 0; // ERROR: inaccessible
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 415
Protected Inheritance Example
1 class Base {
2 public:
3 void func_1();
4 protected:
5 void func_2();
6 private:
7 int x_;
8 };
9
10 class Derived : protected Base {
11 public:
12 void func_3() {
13 func_1(); // OK
14 func_2(); // OK
15 x_ = 0; // ERROR: inaccessible
16 }
17 };
18
19 struct Widget : public Derived {
20 void func_4() { func_2(); } // OK
21 };
22
23 int main() {
24 Derived d; // OK: defaulted constructor is public
25 d.func_1(); // ERROR: inaccessible
26 d.func_2(); // ERROR: inaccessible
27 d.x_ = 0; // ERROR: inaccessible
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 416
Private Inheritance Example
1 class Base {
2 public:
3 void func_1();
4 protected:
5 void func_2();
6 private:
7 int x_;
8 };
9
10 class Derived : private Base {
11 public:
12 void func_3() {
13 func_1(); // OK
14 func_2(); // OK
15 x_ = 0; // ERROR: inaccessible
16 }
17 };
18
19 struct Widget : public Derived {
20 void func_4() { func_2(); } // ERROR: inaccessible
21 };
22
23 int main() {
24 Derived d; // OK: defaulted constructor is public
25 d.func_1(); // ERROR: inaccessible
26 d.func_2(); // ERROR: inaccessible
27 d.x_ = 0; // ERROR: inaccessible
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 417
Public Inheritance
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 418
Public Inheritance Example
1 #include <string>
2
3 class Person {
4 public:
5 Person(const std::string& family_name, const std::string&
6 given_name) : family_name_(family_name),
7 given_name_(given_name) {}
8 std::string family_name() const
9 {return family_name_;}
10 std::string given_name() const
11 {return given_name_;}
12 std::string full_name() const
13 {return family_name_ + ", " + given_name_;}
14 private:
15 std::string family_name_;
16 std::string given_name_;
17 };
18
19 class Student : public Person {
20 public:
21 Student(const std::string& family_name, const std::string&
22 given_name, const std::string& student_id) :
23 Person(family_name, given_name), student_id_(student_id) {}
24 std::string student_id()
25 {return student_id_;}
26 private:
27 std::string student_id_;
28 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 419
Protected and Private Inheritance
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 420
Policy-Based Design Example: Inefficient Memory Usage
1 #include <mutex>
2
3 class ThreadSafePolicy {
4 public:
5 void lock() {mutex_.lock();}
6 void unlock() {mutex_.unlock();}
7 private:
8 std::mutex mutex_;
9 };
10
11 class ThreadUnsafePolicy {
12 public:
13 void lock() {} // no-op
14 void unlock() {} // no-op
15 };
16
17 template<class ThreadSafetyPolicy>
18 class Widget {
19 ThreadSafetyPolicy policy_;
20 // ...
21 };
22
23 int main() {
24 Widget<ThreadUnsafePolicy> w;
25 // w.policy_ has no data members, but
26 // sizeof(w.policy_) >= 1
27 // inefficient use of memory
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 421
Policy-Based Design Example: Private Inheritance and EBO
1 #include <mutex>
2
3 class ThreadSafePolicy {
4 public:
5 void lock() {mutex_.lock();}
6 void unlock() {mutex_.unlock();}
7 private:
8 std::mutex mutex_;
9 };
10
11 class ThreadUnsafePolicy {
12 public:
13 void lock() {} // no-op
14 void unlock() {} // no-op
15 };
16
17 template<class ThreadSafetyPolicy>
18 class Widget : ThreadSafetyPolicy {
19 // ...
20 };
21
22 int main() {
23 Widget<ThreadUnsafePolicy> w;
24 // empty-base optimization (EBO) can be applied
25 // no memory overhead for no-op thread-safety policy
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 422
Inheritance and Constructors
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 423
Inheriting Constructors Example
1 class Base {
2 public:
3 Base() : i_(0.0), j_(0) {}
4 Base(int i) : i_(i), j_(0) {}
5 Base(int i, int j) : i_(i), j_(j) {}
6 // ... (other non-constructor members)
7 private:
8 int i_, j_;
9 };
10
11 class Derived : public Base {
12 public:
13 // inherit non-special constructors from Base
14 // (default constructor not inherited)
15 using Base::Base;
16 // default constructor is implicitly declared and
17 // not inherited
18 };
19
20 int main() {
21 Derived a;
22 // invokes non-inherited Derived::Derived()
23 Derived b(42, 42);
24 // invokes inherited Base::Base(int, int)
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 424
Inheriting Constructors Example
1 class Base {
2 public:
3 Base() : i_(0), j_(0), k_(0) {}
4 Base(int i, int j) : i_(i), j_(j), k_(0) {}
5 Base(int i, int j, int k) : i_(i), j_(j), k_(k) {}
6 // ... (other non-constructor members)
7 private:
8 int i_, j_, k_;
9 };
10
11 class Derived : public Base {
12 public:
13 // inherit non-special constructors from Base
14 // (default constructor not inherited)
15 using Base::Base;
16 // following constructor hides inherited constructor
17 Derived(int i, int j, int k) : Base(-i, -j, -k) {}
18 // no implicitly-generated default constructor
19 };
20
21 int main() {
22 Derived b(1, 2);
23 // invokes inherited Base::Base(int, int)
24 Derived c(1, 2, 3);
25 // invokes Derived::Derived(int, int, int)
26 // following would produce compile-time error:
27 // Derived a; // ERROR: no default constructor
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 425
Inheritance, Assignment Operators, and Destructors
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 426
Inheriting Assignment Operators Example
1 class Base {
2 public:
3 explicit Base(int i) : i_(i) {}
4 Base& operator=(int i) {
5 i_ = i;
6 return *this;
7 }
8 // ...
9 private:
10 int i_;
11 };
12
13 class Derived : public Base {
14 public:
15 // inherit non-special constructors
16 using Base::Base;
17 // inherit non-special assignment operators
18 using Base::operator=;
19 // ...
20 };
21
22 int main() {
23 Derived d(0);
24 // invokes inherited Base::Base(int)
25 d = 42;
26 // invokes inherited Base::operator=(int)
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 427
Construction and Destruction Order
during construction of object, all of its base class objects constructed first
order of construction:
1 base class objects as listed in type definition left to right
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 428
Order of Construction
1 #include <vector>
2 #include <string>
3
4 class Base {
5 public:
6 Base(int n) : v_(n, 0) {}
7 // ...
8 private:
9 std::vector<char> v_;
10 };
11
12 class Derived : public Base {
13 public:
14 Derived(const std::string& s) : Base(1024), s_(s)
15 { i_ = 0; }
16 // ...
17 private:
18 std::string s_;
19 int i_;
20 };
21
22 int main() {
23 Derived d("hello");
24 }
construction order for Derived constructor: 1) Base class object, 2) data
member s_, 3) Derived constructor body (initializes data member i_)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 429
Hiding Base-Class Member Functions in Derived Class
can provide new versions of member functions in derived class to hide
original functions in base class
1 #include <iostream>
2
3 class Fruit {
4 public:
5 void print() const {std::cout << "fruit\n";}
6 };
7
8 class Apple : public Fruit {
9 public:
10 void print() const {std::cout << "apple\n";}
11 };
12
13 class Banana : public Fruit {
14 public:
15 void print() const {std::cout << "banana\n";}
16 };
17
18 int main() {
19 Fruit f;
20 Apple a;
21 Banana b;
22 f.print(); // calls Fruit::print
23 a.print(); // calls Apple::print
24 b.print(); // calls Banana::print
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 430
Upcasting
derived-class object always has base-class subobject
given reference or pointer to derived-class object, may want to find
reference or pointer to corresponding base-class object
upcasting: converting derived-class pointer or reference to base-class
pointer or reference
upcasting allows us to treat derived-class object as base-class object
upcasting always safe in sense that cannot result in incorrect type (since
every derived-class object is also a base-class object)
can upcast without explicit type-cast operator as long as casted-to type is
accessible; C-style cast can used to bypass access protection (although
not recommended)
example:
class Base { /* ... */ };
class Derived : public Base { /* ... */ };
void func() {
Derived d;
Base* bp = &d;
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 431
Downcasting
downcasting: converting base-class pointer or reference to derived-class
pointer or reference
downcasting allows us to force base-class object to be treated as
derived-class object
downcasting is not always safe (since not every base-class object is
necessarily also derived-class object)
must only downcast when known that object actually has derived type
(except in case of dynamic_cast)
downcasting always requires explicit cast (e.g., static_cast,
dynamic_cast for dynamically-checked cast in polymorphic case, or
C-style cast)
example:
class Base { /* ... (nonpolymorphic) */ };
class Derived : public Base { /* ... */ };
void func() {
Derived d;
Base* bp = &d;
Derived* dp = static_cast<Derived*>(bp);
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 432
Upcasting/Downcasting Example
1 class Base { /* ... (nonpolymorphic) */ };
2
3 class Derived : public Base { /* ... */ };
4
5 int main() {
6 Base b;
7 Derived d;
8 Base* bp = nullptr;
9 Derived* dp = nullptr;
10 bp = &d;
11 // OK: upcast does not require explicit cast
12 dp = bp;
13 // ERROR: downcast requires explicit cast
14 dp = static_cast<Derived*>(bp);
15 // OK: downcast with explicit cast and
16 // pointer (bp) refers to Derived object
17 Base& br = d;
18 // OK: upcast does not require explicit cast
19 Derived& dr1 = *bp;
20 // ERROR: downcast requires explicit cast
21 Derived& dr2 = *static_cast<Derived*>(bp);
22 // OK: downcast with explicit cast and
23 // object (*bp) is of Derived type
24 dp = static_cast<Derived*>(&b);
25 // BUG: pointer (&b) does not refer to Derived object
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 433
Upcasting Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 434
Nonpolymorphic Behavior
1 #include <iostream>
2 #include <string>
3
4 class Person {
5 public:
6 Person(const std::string& family, const std::string& given) :
7 family_(family), given_(given) {}
8 void print() const {std::cout << "person: " << family_ << ’,’ << given_ << ’\n’;}
9 protected:
10 std::string family_; // family name
11 std::string given_; // given name
12 };
13
14 class Student : public Person {
15 public:
16 Student(const std::string& family, const std::string& given,
17 const std::string& id) : Person(family, given), id_(id) {}
18 void print() const {
19 std::cout << "student: " << family_ << ’,’ << given_ << ’,’ << id_ << ’\n’;
20 }
21 private:
22 std::string id_; // student ID
23 };
24
25 void processPerson(const Person& p) {
26 p.print(); // always calls Person::print
27 // ...
28 }
29
30 int main() {
31 Person p("Ritchie", "Dennis");
32 Student s("Doe", "John", "12345678");
33 processPerson(p); // invokes Person::print
34 processPerson(s); // invokes Person::print
35 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 436
Inheritance and Overloading
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 437
Inheritance and Overloading Example
1 #include<iostream>
2
3 class Base {
4 public:
5 double f(double d) const {return d;}
6 // ...
7 };
8
9 class Derived : public Base {
10 public:
11 int f(int i) const {return i;}
12 // ...
13 };
14
15 int main()
16 {
17 Derived d;
18 std::cout << d.f(0) << ’\n’;
19 // calls Derived::f(int) const
20 std::cout << d.f(0.5) << ’\n’;
21 // calls Derived::f(int) const; probably not intended
22 Derived* dp = &d;
23 std::cout << dp->f(0) << ’\n’;
24 // calls Derived::f(int) const
25 std::cout << dp->f(0.5) << ’\n’;
26 // calls Derived::f(int) const; probably not intended
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 438
Using Base Members Example
1 #include<iostream>
2
3 class Base {
4 public:
5 double f(double d) const {return d;}
6 // ...
7 };
8
9 class Derived : public Base {
10 public:
11 using Base::f; // bring Base::f into scope
12 int f(int i) const {return i;}
13 // ...
14 };
15
16 int main()
17 {
18 Derived d;
19 std::cout << d.f(0) << ’\n’;
20 // calls Derived::f(int) const
21 std::cout << d.f(0.5) << ’\n’;
22 // calls Base::f(double) const
23 Derived* dp = &d;
24 std::cout << dp->f(0) << ’\n’;
25 // calls Derived::f(int) const
26 std::cout << dp->f(0.5) << ’\n’;
27 // calls Base::f(double) const
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 439
Inheritance, Templates, and Name Lookup
name lookup in templates takes place in two phases:
1 at template definition time
2 at template instantiation time
at template definition time, compiler parses template and looks up any
nondependent names
result of nondependent name lookup must be identical in all instantiations
of template (since, by definition, nondependent name does not depend on
template parameter)
at template instantiation time, compiler looks up any dependent names
results of dependent name lookup can differ from one template
instantiation to another (since, by definition, dependent name depends on
template parameters)
two-phase name lookup can interact with inheritance in ways that can
sometimes lead to unexpected problems in code
may need to add “this->” or employ using statement to make name
dependent (when it would otherwise be nondependent)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 440
Name Lookup Example (Incorrect Code)
1 #include <iostream>
2
3 template <class T>
4 struct Base {
5 using Real = T;
6 Base(Real x_ = Real()) : x(x_) {}
7 void f() {std::cout << x << "\n";};
8 Real x;
9 };
10
11 template <class T>
12 struct Derived : Base<T> {
13 Derived(Real y_ = Real()) : y(y_) {}
14 // ERROR: Real (which is nondependent and looked up at
15 // template definition time) is assumed to be defined
16 // outside class
17 void g() {
18 x = y;
19 // ERROR: x assumed to be object outside class
20 f();
21 // ERROR: f assumed to be function outside class
22 }
23 Real y;
24 };
25
26 int main() {
27 Derived<double> w(0.0);
28 w.g();
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 441
Name Lookup Example (Correct Code)
1 #include <iostream>
2
3 template <class T>
4 struct Base {
5 using Real = T;
6 Base(Real x_ = Real()) : x(x_) {}
7 void f() {std::cout << x << "\n";};
8 Real x;
9 };
10
11 template <class T>
12 struct Derived : Base<T> {
13 using Real = typename Base<T>::Real;
14 // OK: Base<T>::Real dependent
15 Derived(Real y_ = Real()) : y(y_) {}
16 void g() {
17 this->x = y; // OK: this->x dependent
18 this->f(); // OK: this->f() dependent
19 }
20 Real y;
21 };
22
23 int main() {
24 Derived<double> w(0.0);
25 w.g();
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 442
Section 2.7.2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 443
Run-Time Polymorphism
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 445
Virtual Functions (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 446
Virtual Function Example
1 #include <iostream>
2 #include <string>
3
4 class Person {
5 public:
6 Person(const std::string& family, const std::string& given) :
7 family_(family), given_(given) {}
8 virtual void print() const
9 {std::cout << "person: " << family_ << ’,’ << given_ << ’\n’;}
10 protected:
11 std::string family_; // family name
12 std::string given_; // given name
13 };
14
15 class Student : public Person {
16 public:
17 Student(const std::string& family, const std::string& given,
18 const std::string& id) : Person(family, given), id_(id) {}
19 void print() const {
20 std::cout << "student: " << family_ << ’,’ << given_ << ’,’ << id_ << ’\n’;
21 }
22 private:
23 std::string id_; // student ID
24 };
25
26 void processPerson(const Person& p) {
27 p.print(); // polymorphic function call
28 // ...
29 }
30
31 int main() {
32 Person p("Ritchie", "Dennis");
33 Student s("Doe", "John", "12345678");
34 processPerson(p); // invokes Person::print
35 processPerson(s); // invokes Student::print
36 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 447
Override Control: The override Qualifier
when looking at code for derived class, often not possible to determine if
member function intended to override virtual function in base class (or one
of its base classes)
can sometimes lead to bugs where programmer expects member function
to override virtual function when function not virtual
override qualifier used to indicate that member function is expected to
override virtual function in parent class; must come at end of function
declaration
example:
class Person {
public:
virtual void print() const;
// ...
};
class Employee : public Person {
public:
void print() const override; // must be virtual
// ...
};
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 448
Override Control: The final Qualifier
sometimes, may want to prevent any further overriding of virtual function
in any subsequent derived classes
adding final qualifier to declaration of virtual function prevents function
from being overridden in any subsequent derived classes
preventing further overriding can sometimes allow for better optimization
by compiler (e.g., via devirtualization)
example:
class A {
public:
virtual void doStuff();
// ...
};
class B : public A {
public:
void doStuff() final; // prevent further overriding
// ...
};
class C : public B {
public:
void doStuff(); // ERROR: cannot override
// ...
};
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 449
final Qualifier Example
1 class Worker {
2 public:
3 virtual void prepareEnvelope();
4 // ...
5 };
6
7 class SpecialWorker : public Worker {
8 public:
9 // prevent overriding function responsible for
10 // overall envelope preparation process
11 // but allow functions for individual steps in
12 // process to be overridden
13 void prepareEnvelope() final {
14 stuffEnvelope(); // step 1
15 lickEnvelope(); // step 2
16 sealEnvelope(); // step 3
17 }
18 virtual void stuffEnvelope();
19 virtual void lickEnvelope();
20 virtual void sealEnvelope();
21 // ...
22 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 450
Constructors, Destructors, and Virtual Functions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 451
Problematic Code with Non-Virtual Destructor
1 class Base {
2 public:
3 Base() {}
4 ˜Base() {} // non-virtual destructor
5 // ...
6 };
7
8 class Derived : public Base {
9 public:
10 Derived() : buffer_(new char[10’000]) {}
11 ˜Derived() {delete[] buffer_;}
12 // ...
13 private:
14 char* buffer_;
15 };
16
17 void process(Base* bp) {
18 // ...
19 delete bp; // always invokes only Base::˜Base
20 }
21
22 int main() {
23 process(new Base);
24 process(new Derived); // leaks memory
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 452
Corrected Code with Virtual Destructor
1 class Base {
2 public:
3 Base() {}
4 virtual ˜Base() {} // virtual destructor
5 // ...
6 };
7
8 class Derived : public Base {
9 public:
10 Derived() : buffer_(new char[10’000]) {}
11 ˜Derived() {delete[] buffer_;}
12 // ...
13 private:
14 char* buffer_;
15 };
16
17 void process(Base* bp) {
18 // ...
19 delete bp; // invokes destructor polymorphically
20 }
21
22 int main() {
23 process(new Base);
24 process(new Derived);
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 453
Preventing Creation of Derived Classes
might want to prevent deriving from class with destructor that is not virtual
preventing derivation can sometimes also facilitate better compiler
optimization (e.g., via devirtualization)
might want to prevent derivation so that objects can be copied safely
without fear of slicing
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 454
Covariant Return Type
1 class Base {
2 public:
3 virtual Base* clone() const {
4 return new Base(*this);
5 }
6 // ...
7 };
8
9 class Derived : public Base {
10 public:
11 // use covariant return type
12 Derived* clone() const override {
13 return new Derived(*this);
14 }
15 // ...
16 };
17
18 int main() {
19 Derived* d = new Derived;
20 Derived* d2 = d->clone();
21 // OK: return type is Derived*
22 // without covariant return type, would need cast:
23 // Derived* d2 = static_cast<Derived*>(d->clone());
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 456
Pure Virtual Functions
pure virtual function can still be defined, although likely only useful in case
of virtual destructor
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 457
Abstract Classes
class with one or more pure virtual functions called abstract class
cannot directly instantiate objects of abstract class (can only use them as
base class objects)
class that derives from abstract class need not override all of its pure
virtual methods
class that does not override all pure virtual methods of abstract base class
will also be abstract
most commonly, abstract classes have no state (i.e., data members) and
used to provide interfaces, which can be inherited by other classes
if class has no pure virtual functions and abstract class is desired, can
make destructor pure virtual (but must provide definition of destructor
since invoked by derived classes)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 458
Abstract Class Example
1 #include <cmath>
2
3 class Shape {
4 public:
5 virtual bool isPolygon() const = 0;
6 virtual float area() const = 0;
7 virtual ˜Shape() {};
8 };
9
10 class Rectangle : public Shape {
11 public:
12 Rectangle(float w, float h) : w_(w), h_(h) {}
13 bool isPolygon() const override {return true;}
14 float area() const override {return w_ * h_;}
15 private:
16 float w_; // width of rectangle
17 float h_; // height of rectangle
18 };
19
20 class Circle : public Shape {
21 public:
22 Circle(float r) : r_(r) {}
23 float area() const override {return M_PI * r_ * r_;}
24 bool isPolygon() const override {return false;}
25 private:
26 float r_; // radius of circle
27 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 459
Pure Virtual Destructor Example
1 class Abstract {
2 public:
3 virtual ˜Abstract() = 0; // pure virtual destructor
4 // ... (no other virtual functions)
5 };
6
7 inline Abstract::˜Abstract()
8 { /* possibly empty */ }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 460
The dynamic_cast Operator
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 463
Curiously-Recurring Template Pattern (CRTP)
when derived type known at compile time, may want behavior similar to
virtual functions but without run-time cost (by performing binding at
compile time instead of run time)
can be achieved with technique known as curiously-recurring template
pattern (CRTP)
class Derived derives from class template instantiation using Derived
itself as template argument
example:
template <class Derived>
class Base {
// ...
};
class Derived : public Base<Derived> {
// ...
};
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 464
CRTP Example: Static Polymorphism
1 #include <iostream>
2
3 template <class Derived>
4 class Base {
5 public:
6 void interface() {
7 std::cout << "Base::interface called\n";
8 static_cast<Derived*>(this)->implementation();
9 }
10 // ...
11 };
12
13 class Derived : public Base<Derived> {
14 public:
15 void implementation() {
16 std::cout << "Derived::implementation called\n";
17 }
18 // ...
19 };
20
21 int main() {
22 Derived d;
23 d.interface();
24 // calls Base::interface which, in turn, calls
25 // Derived::implementation
26 // no virtual function call, however
27
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 465
CRTP Example: Static Polymorphism
1 class TreeNode {
2 public:
3 enum Kind {RED, BLACK}; // kinds of nodes
4 TreeNode *left(); // get left child node
5 TreeNode *right(); // get right child node
6 Kind kind(); // get kind of node
7 // ...
8 };
9
10 template <class Derived>
11 class GenericVisitor {
12 public:
13 void visit_preorder(TreeNode* node) {
14 if (node) {
15 process_node(node);
16 visit_preorder(node->left());
17 visit_preorder(node->right());
18 }
19 }
20 void visit_inorder(TreeNode* node) { /* ... */ }
21 void visit_postorder(TreeNode* node) { /* ... */ }
22 void process_red_node(TreeNode* node) { /* ... */ };
23 void process_black_node(TreeNode* node) { /* ... */ };
24 private:
25 Derived& derived() {return *static_cast<Derived*>(this);}
26 void process_node(TreeNode* node) {
27 if (node->kind() == TreeNode::RED) {
28 derived().process_red_node(node);
29 } else {
30 derived().process_black_node(node);
31 }
32 }
33 };
34
35 class SpecialVisitor : public GenericVisitor<SpecialVisitor> {
36 public:
37 void process_red_node(TreeNode* node) { /* ... */ }
38 };
39
40 int main() {SpecialVisitor v;}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 466
CRTP Example: Comparisons
1 #include <cassert>
2
3 template<class Derived>
4 struct Comparisons {
5 friend bool operator==(const Comparisons<Derived>& x,
6 const Comparisons<Derived>& y) {
7 const Derived& xr = static_cast<const Derived&>(x);
8 const Derived& yr = static_cast<const Derived&>(y);
9 return !(xr < yr) && !(yr < xr);
10 }
11 // operator!= and others
12 };
13
14 class Widget : public Comparisons<Widget> {
15 public:
16 Widget(bool b, int i) : b_(b), i_(i) {}
17 friend bool operator<(const Widget& x, const Widget& y)
18 {return x.i_ < y.i_;}
19 private:
20 bool b_;
21 int i_;
22 };
23
24 int main() {
25 Widget w1(true, 1);
26 Widget w2(false, 1);
27 assert(w1 == w2);
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 467
CRTP Example: Object Counting
1 #include <iostream>
2 #include <cstdlib>
3
4 template <class T>
5 class Counter {
6 public:
7 Counter() {++count_;}
8 Counter(const Counter&) {++count_;}
9 ˜Counter() {--count_;}
10 static std::size_t howMany() {return count_;}
11 private:
12 static std::size_t count_;
13 };
14
15 template <class T>
16 std::size_t Counter<T>::count_ = 0;
17
18 // inherit from Counter to count objects
19 class Widget: private Counter<Widget> {
20 public:
21 using Counter<Widget>::howMany;
22 // ...
23 };
24
25 int main() {
26 Widget w1; int c1 = Widget::howMany();
27 Widget w2, w3; int c2 = Widget::howMany();
28 std::cout << c1 << ’ ’ << c2 << ’\n’;
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 468
Section 2.7.3
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 469
Multiple Inheritance
language allows derived class to inherit from more than one base class
multiple inheritance (MI): deriving from more than one base class
although multiple inheritance not best solution for most problems, does
have some compelling use cases
one compelling use case is for inheriting interfaces by deriving from
abstract base classes with no data members
when misused, multiple inheritance can lead to very convoluted code
in multiple inheritance contexts, ambiguities in naming can arise
for example, if class Derived inherits from classes Base1 and Base2,
each of which have member called x, name x can be ambiguous in some
contexts
scope resolution operator can be used to resolve ambiguous names
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 470
Ambiguity Resolution Example
1 class Base1 {
2 public:
3 void func();
4 // ...
5 };
6
7 class Base2 {
8 void func();
9 // ...
10 };
11
12 class Derived : public Base1, public Base2 {
13 public:
14 // ...
15 };
16
17 int main() {
18 Derived d;
19 d.func(); // ERROR: ambiguous function call
20 d.Base1::func(); // OK: invokes Base1::func
21 d.Base2::func(); // OK: invokes Base2::func
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 471
Multiple Inheritance Example
1 class Input_stream {
2 public:
3 virtual ˜Input_stream() {}
4 virtual int read_char() = 0;
5 virtual int read(char* buffer, int size) = 0;
6 virtual bool is_input_ready() const = 0;
7 // ...(all pure virtual, no data)
8 };
9
10 class Output_stream {
11 public:
12 virtual ˜Output_stream() {}
13 virtual int write_char(char c) = 0;
14 virtual int write(char* buffer, int size) = 0;
15 virtual int flush_output() = 0;
16 // ... (all pure virtual, no data)
17 };
18
19 class Input_output_stream : public Input_stream,
20 public Output_stream {
21 // ...
22 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 472
Dreaded Diamond Inheritance Pattern
B C
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 473
Dreaded Diamond Example
1 class Base {
2 public:
3 // ...
4 protected:
5 int data_;
6 };
7
8 class D1 : public Base { /* ... */ };
9
10 class D2 : public Base { /* ... */ };
11
12 class Join : public D1, public D2 {
13 public:
14 void method() {
15 data_ = 1; // ERROR: ambiguous
16 D1::data_ = 1; // OK: unambiguous
17 }
18 };
19
20 int main() {
21 Join* j = new Join();
22 Base* b;
23 b = j; // ERROR: ambiguous
24 b = static_cast<D1*>(j); // OK: unambiguous
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 474
Virtual Inheritance
when using multiple inheritance, may want to ensure that only one
instance of base-class object can appear in derived-class object
virtual base class: base class that is only ever included once in derived
class, even if derived from multiple times
virtual inheritance: when derived class inherits from base class that is
virtual
virtual inheritance can be used to avoid situations like dreaded diamond
pattern
order of construction: virtual base classes constructed first in depth-first
left-to-right traversal of graph of base classes, where left-to-right refers to
order of appearance of base class names
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 475
Avoiding Dreaded Diamond With Virtual Inheritance
1 class Base {
2 public:
3 // ...
4 protected:
5 int data_;
6 };
7
8 class D1 : public virtual Base { /* ... */ };
9
10 class D2 : public virtual Base { /* ... */ };
11
12 class Join : public D1, public D2 {
13 public:
14 void method() {
15 data_ = 1; // OK: unambiguous
16 }
17 };
18
19 int main() {
20 Join* j = new Join();
21 Base* b = j; // OK: unambiguous
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 476
Section 2.7.4
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 477
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 478
Section 2.8
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 479
C++ Standard Library
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 480
C++ Standard Library (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 481
Commonly-Used Header Files
Language-Support Library
Header File Description
cstdlib run-time support, similar to stdlib.h from C
(e.g., exit)
limits properties of fundamental types (e.g.,
numeric_limits)
exception exception handling support (e.g.,
set_terminate, current_exception)
initializer_list initializer_list class template
Diagnostics Library
Header File Description
cassert assertions (e.g., assert)
stdexcept predefined exception types (e.g., invalid_argument,
domain_error, out_of_range)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 482
Commonly-Used Header Files (Continued 1)
General-Utilities Library
Header File Description
utility basic function and class templates (e.g., swap, move,
pair)
memory memory management (e.g., unique_ptr, shared_ptr,
addressof)
functional functors (e.g., less, greater)
type_traits type traits (e.g., is_integral, is_reference)
chrono clocks (e.g., system_clock, steady_clock,
high_resolution_clock)
Strings Library
Header File Description
string C++ string classes (e.g., string)
cstring C-style strings, similar to string.h from C (e.g., strlen)
cctype character classification, similar to ctype.h from C (e.g.,
isdigit, isalpha)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 483
Commonly-Used Header Files (Continued 2)
Numerics Library
Header File Description
cmath C math library, similar to math.h from C (e.g., sin, cos)
complex complex numbers (e.g., complex)
numeric generalized numeric operations (e.g., gcd, lcm,
inner_product)
random random number generation (e.g.,
uniform_int_distribution,
uniform_real_distribution,
normal_distribution)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 485
Commonly-Used Header Files (Continued 4)
I/O Library
Header File Description
iostream iostream objects (e.g., cin, cout, cerr)
istream input streams (e.g., istream)
ostream output streams (e.g., ostream)
ios base classes and other declarations for streams
(e.g., ios_base, hex, fixed)
fstream file streams (e.g., fstream)
sstream string streams (e.g., stringstream)
iomanip manipulators (e.g., setw, setprecision)
Regular-Expressions Library
Header File Description
regexp regular expressions (e.g., basic_regex)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 486
Commonly-Used Header Files (Continued 5)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 487
Section 2.8.1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 488
Standard Template Library (STL)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 489
Containers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 490
Sequence Containers and Container Adapters
Sequence Containers
Name Description
array fixed-size array
vector dynamic-size array
deque double-ended queue
forward_list singly-linked list
list doubly-linked list
Container Adapters
Name Description
stack stack
queue FIFO queue
priority_queue priority queue
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 491
Associative Containers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 492
Typical Sequence Container Member Functions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 493
Container Example
1 #include <iostream>
2 #include <vector>
3
4 int main() {
5 std::vector<int> values;
6
7 // append elements with values 0 to 9
8 for (int i = 0; i < 10; ++i) {
9 values.push_back(i);
10 }
11
12 // print each element followed by space
13 for (int i = 0; i < values.size(); ++i) {
14 std::cout << values[i] << ’ ’;
15 }
16 std::cout << ’\n’;
17 }
18
19 /* This program produces the following output:
20 0 1 2 3 4 5 6 7 8 9
21 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 494
Motivation for Iterators
begin end
organization of elements in doubly-linked list container:
begin end
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 495
Motivation for Iterators (Continued)
begin end
suppose we want to set all elements in container to zero
we could use code like:
// int* begin; int* end;
for (int* iter = begin; iter != end; ++iter)
*iter = 0;
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 496
Iterators
iterator: object that allows iteration over collection of elements, where
elements are often (but not necessarily) in container
iterators support many of same operations as pointers
in some cases, iterator may actually be pointer; more frequently, iterator is
user-defined type
five different categories of iterators: 1) input, 2) output, 3) forward,
4) bidirectional, and 5) random access
iterator has particular level of functionality, depending on category
one of three possibilities of access order:
1 forward (i.e., one direction only)
2 forward and backward
3 any order (i.e., random access)
one of three possibilities in terms of read/write access:
1 can only read referenced element (once or multiple times)
2 can only write referenced element (once or multiple times)
3 can read and write referenced element (once or multiple times)
const and mutable (i.e., non-const) variants (i.e., read-only or read/write
access, respectively)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 497
Abilities of Iterator Categories
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 498
Input Iterators
Expression Effect
T(a) copies iterator (copy constructor)
*a dereference as rvalue (i.e., read only); cannot
a->m dereference at old position
++a steps forward (returns new position)
a++ steps forward
a == b test for equality
a != b test for inequality
not assignable (i.e., no assignment operator)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 499
Output Iterators
Expression Effect
T(a) copies iterator (copy constructor)
*a dereference as lvalue (i.e., write only); can only
a->m be dereferenced once; cannot dereference at old
position
++a steps forward (returns new position)
a++ steps forward (returns old position)
not assignable (i.e., no assignment operator)
no comparison operators (i.e., operator==, operator!=)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 500
Forward Iterators
Expression Effect
T() default constructor
T(a) copy constructor
a = b assignment
*a dereference
a->m
++a steps forward (returns new position)
a++ steps forward (returns old position)
a == b test for equality
a != b test for inequality
must ensure that valid to dereference iterator before doing so
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 501
Bidirectional Iterators
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 502
Random-Access Iterators
random access iterators provide all functionality of bidirectional iterators
as well as providing random access to elements
random access iterators provide all functionality of bidirectional iterators
as well as those listed in table below
Expression Effect
a[n] dereference element at index n (where n can be nega-
tive)
a += n steps n elements forward (where n can be negative)
a -= n steps n elements backward (where n can be negative)
a + n iterator for nth next element
n + a iterator for nth next element
a - n iterator for nth previous element
a - b distance from a to b
a < b test if a before b
a > b test if a after b
a <= b test if a not after b
a >= b test if a not before b
pointers (built into language) are examples of random-access iterators
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 503
Iterator Example
1 #include <iostream>
2 #include <vector>
3
4 int main() {
5 std::vector<int> values(10);
6
7 std::cout << "number of elements: " <<
8 (values.end() - values.begin()) << ’\n’;
9
10 // initialize elements of vector to 0, 1, 2, ...
11 for (std::vector<int>::iterator i = values.begin();
12 i != values.end(); ++i) {
13 *i = i - values.begin();
14 }
15
16 // print elements of vector
17 for (std::vector<int>::const_iterator i =
18 values.cbegin(); i != values.cend(); ++i) {
19 std::cout << ’ ’ << *i;
20 }
21 std::cout << ’\n’;
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 504
Iterator Gotchas
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 505
Algorithms
http://en.cppreference.com/w/cpp/algorithm
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 506
Functions
Non-Modifying Sequence Operations
Name Description
all_of test if condition true for all elements in range
any_of test if condition true for any element in range
none_of test if condition true for no elements in range
for_each apply function to range
for_each_n apply function to first n elements in sequence
find find values in range
find_if find element in range
find_if_not find element in range (negated)
find_end find last subsequence in range
find_first_of find element from set in range
adjacent_find find equal adjacent elements in range
count count appearances of value in range
count_if count number of elements in range satisfying condition
mismatch get first position where two ranges differ
equal test whether elements in two ranges differ
search find subsequence in range
search_n find succession of equal values in range
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 507
Functions (Continued 1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 508
Functions (Continued 2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 509
Functions (Continued 3)
Partition Operations
Name Description
is_partitioned test if range is partitioned by predicate
partition partition range in two
partition_copy copies range partition in two
stable_partition partition range in two (stable ordering)
partition_point get partition point
Sorting
Name Description
is_sorted test if range is sorted
is_sorted_until find first unsorted element in range
sort sort elements in range
stable_sort sort elements in range, preserving order of
equivalents
partial_sort partially sort elements in range
partial_sort_copy copy and partially sort range
nth_element sort element in range
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 510
Functions (Continued 4)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 511
Functions (Continued 5)
Heap Operations
Name Description
is_heap test if range is heap
is_heap_until first first element not in heap order
push_heap push element into heap range
pop_heap pop element from heap range
make_heap make heap from range
sort_heap sort elements of heap
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 512
Functions (Continued 6)
Minimum/Maximum
Name Description
min get minimum of given values
max get maximum of given values
minmax get minimum and maximum of given values
min_element get smallest element in range
max_element get largest element in range
minmax_element get smallest and largest elements in range
clamp clamp value between pair of boundary values
lexicographic_compare lexicographic less-than comparison
is_permutation test if range permutation of another
next_permutation transform range to next permutation
prev_permutation transform range to previous permutation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 513
Functions (Continued 7)
Numeric Operations
Name Description
iota fill range with successive values
accumulate accumulate values in range
adjacent_difference compute adjacent difference of range
inner_product compute inner product of range
partial_sum compute partial sums of range
reduce similar to accumulate except out of order
exclusive_scan similar to partial_sum, excludes ith input el-
ement from ith sum
inclusive_scan similar to partial_sum, includes ith input el-
ement in ith sum
transform_reduce applies functor, then reduces out of order
transform_exclusive_scan applies functor then, calculates exclusive
scan
transform_inclusive_scan applies functor, then calculates inclusive scan
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 514
Functions (Continued 8)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 515
Functions (Continued 9)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 516
Functions (Continued 10)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 517
Algorithms Example
1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4 #include <random>
5
6 int main() {
7 std::vector<int> values;
8
9 int x;
10 while (std::cin >> x) {values.push_back(x);}
11
12 std::cout << "zero count: " << std::count(
13 values.begin(), values.end(), 0) << ’\n’;
14
15 std::default_random_engine engine;
16 std::shuffle(values.begin(), values.end(), engine);
17 std::cout << "random order:";
18 for (auto i : values) {std::cout << ’ ’ << i;}
19 std::cout << ’\n’;
20
21 std::sort(values.begin(), values.end());
22 std::cout << "sorted order:";
23 for (auto i : values) {std::cout << ’ ’ << i;}
24 std::cout << ’\n’;
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 518
Prelude to Functor Example
consider std::transform function template:
template <class InputIterator, class OutputIterator,
class UnaryOperator>
OutputIterator transform(InputIterator first,
InputIterator last, OutputIterator result,
UnaryOperator op);
applies op to each element in range [first,last) and stores each
returned value in range beginning at result
std::transform might be written as:
template <class InputIterator, class OutputIterator,
class UnaryOperator>
OutputIterator transform(InputIterator first,
InputIterator last, OutputIterator result,
UnaryOperator op) {
while (first != last) {
*result = op(*first);
++first;
++result;
}
return result;
}
op is entity that can be used with function call syntax (i.e., function or
functor)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 519
Functor Example
1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4
5 struct MultiplyBy { // Functor class
6 MultiplyBy(double factor) : factor_(factor) {}
7 double operator()(double x) const
8 {return factor_ * x;}
9 private:
10 double factor_; // multiplicative factor
11 };
12
13 int main() {
14 MultiplyBy mb(2.0);
15 std::vector v{1.0, 2.0, 3.0};
16 // v contains 1 2 3
17 std::transform(v.begin(), v.end(), v.begin(), mb);
18 // v contains 2 4 6
19 for (auto i : v) {std::cout << i << ’\n’;}
20 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 520
Section 2.8.2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 521
The std::array Class Template
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 522
Member Types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 523
Member Functions
Iterators
Member Name Description
begin return iterator to beginning
end return iterator to end
cbegin return const iterator to beginning
cend return const iterator to end
rbegin return reverse iterator to beginning
rend return reverse iterator to end
crbegin return const reverse iterator to beginning
crend return const reverse iterator to end
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 524
Member Functions (Continued 1)
Capacity
Member Name Description
empty test if array is empty
size return size
max_size return maximum size
Element Access
Member Name Description
operator[] access element (no bounds checking)
at access element (with bounds checking)
front access first element
back access last element
data return pointer to start of element data
Modifiers
Member Name Description
fill fill container with specified value
swap swap contents of two arrays
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 525
array Example
1 #include <array>
2 #include <iostream>
3 #include <algorithm>
4 #include <experimental/iterator>
5
6 int main() {
7 std::array<int, 3> a1{3, 1, 2};
8 std::array<int, 3> a2;
9 a2.fill(42);
10 for (auto i : a2) {
11 std::cout << i << ’\n’;
12 }
13 a2 = a1;
14 std::sort(a1.begin(), a1.end());
15 std::copy(a1.begin(), a1.end(),
16 std::experimental::make_ostream_joiner(std::cout, ", "));
17 std::cout << ’\n’;
18 for(auto i = a2.begin(); i != a2.end(); ++i) {
19 std::cout << *i;
20 if (i != a2.end() - 1) {std::cout << ", ";}
21 }
22 std::cout << ’\n’;
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 526
array Example
1 #include <array>
2 #include <iostream>
3 #include <algorithm>
4
5 int main() {
6 // Fixed-size array with 4 elements.
7 std::array<int, 4> a{2, 4, 3, 1};
8
9 // Print elements of array.
10 for (auto i = a.cbegin(); i != a.cend(); ++i) {
11 std::cout << ’ ’ << *i;
12 }
13 std::cout << ’\n’;
14
15 // Sort elements of array.
16 std::sort(a.begin(), a.end());
17
18 // Print elements of array.
19 for (auto i = a.cbegin(); i != a.cend(); ++i) {
20 std::cout << ’ ’ << *i;
21 }
22 std::cout << ’\n’;
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 527
Section 2.8.3
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 528
The std::vector Class Template
http://en.cppreference.com/w/cpp/container/vector
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 529
Member Types
Member Type Description
value_type T (i.e., element type)
allocator_type Allocator (i.e., allocator)
size_type type used for measuring size (typically unsigned in-
tegral type)
difference_type type used to measure distance (typically signed in-
tegral type)
reference value_type&
const_reference const value_type&
pointer allocator_traits<Allocator>::pointer
const_pointer allocator_traits<Allocator>::
const_pointer
iterator random-access iterator type
const_iterator const random-access iterator type
reverse_iterator reverse iterator type
(reverse_iterator<iterator>)
const_reverse_iterator const reverse iterator type
(reverse_iterator<const_iterator>)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 530
Member Functions
Construction, Destruction, and Assignment
Member Name Description
constructor construct vector (overloaded)
destructor destroy vector
operator= assign vector
Iterators
Member Name Description
begin return iterator to beginning
end return iterator to end
cbegin return const iterator to beginning
cend return const iterator to end
rbegin return reverse iterator to beginning
rend return reverse iterator to end
crbegin return const reverse iterator to beginning
crend return const reverse iterator to end
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 531
Member Functions (Continued 1)
Capacity
Member Name Description
empty test if vector is empty
size return size
max_size return maximum size
capacity return allocated storage capacity
reserve request change in capacity
shrink_to_fit shrink to fit
Element Access
Member Name Description
operator[] access element (no bounds checking)
at access element (with bounds checking)
front access first element
back access last element
data return pointer to start of element data
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 532
Member Functions (Continued 2)
Modifiers
Member Name Description
clear clear content
assign assign vector content
insert insert elements
emplace insert element, constructing in place
push_back add element at end
emplace_back insert element at end, constructing in place
erase erase elements
pop_back delete last element
resize change size
swap swap content of two vectors
Allocator
Member Name Description
get_allocator get allocator used by vector
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 533
Invalidation of References, Iterators, and Pointers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 534
Iterator Invalidation Example
start denotes pointer to first element in array holding elements of vector
i is iterator for vector (e.g., vector<T>::const_iterator or vector<T>::iterator)
initial vector has three elements and capacity of three
a b c
start i
push_back(d) invoked
new larger array is allocated (say, twice size of original), contents of old array
moved to new one, and then new element added
? ? ? a b c d unused unused
i start
elements in old array destroyed and memory for old array deallocated; iterator i
is now invalid:
a b c d unused unused
i start
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 535
vector Example: Constructors
std::vector<double> v0;
// empty vector
std::vector<double> v1(10);
// vector with 10 elements, default constructed
// (which for double means uninitialized)
std::vector<double> v2(10, 5.0);
// vector with 10 elements, each initialized to 5.0
std::vector<int> v3{1, 2, 3};
// vector with 3 elements: 1, 2, 3
// std::initializer_list (note brace brackets)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 536
vector Example: Iterators
1 #include <iostream>
2 #include <vector>
3
4 int main() {
5 std::vector v{0, 1, 2, 3};
6 for (auto& i : v) {++i;}
7 for (auto i : v) {
8 std::cout << ’ ’ << i;
9 }
10 std::cout << ’\n’;
11 for (auto i = v.begin(); i != v.end(); ++i) {
12 --(*i);
13 }
14 for (auto i = v.cbegin(); i != v.cend(); ++i) {
15 std::cout << ’ ’ << *i;
16 }
17 std::cout << ’\n’;
18 for (auto i = v.crbegin(); i != v.crend(); ++i) {
19 std::cout << ’ ’ << *i;
20 }
21 std::cout << ’\n’;
22 }
program output:
1 2 3 4
0 1 2 3
3 2 1 0
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 537
vector Example
1 #include <iostream>
2 #include <vector>
3
4 int main() {
5 std::vector<double> values;
6 // ...
7
8 // Erase all elements and then read elements from
9 // standard input.
10 values.clear();
11 double x;
12 while (std::cin >> x) {
13 values.push_back(x);
14 }
15 std::cout << "number of values read: " <<
16 values.size() << ’\n’;
17
18 // Loop over all elements and print the number of
19 // negative elements found.
20 int count = 0;
21 for (auto i = values.cbegin(); i != values.cend(); ++i) {
22 if (*i < 0.0) {
23 ++count;
24 }
25 }
26 std::cout << "number of negative values: " << count <<
27 ’\n’;
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 538
vector Example: Emplace
1 #include <iostream>
2 #include <vector>
3
4 int main() {
5 std::vector<std::vector<int>> v{{1, 2, 3}, {4, 5, 6}};
6 v.emplace_back(10, 0);
7 // The above use of emplace_back is more efficient than:
8 // v.push_back(std::vector<int>(10, 0));
9 for (const auto& i : v) {
10 for (const auto& j : i) {
11 std::cout << ’ ’ << j;
12 }
13 std::cout << ’\n’;
14 }
15 }
program output:
1 2 3
4 5 6
0 0 0 0 0 0 0 0 0 0
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 539
Section 2.8.4
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 540
The std::basic_string Class Template
character string type, parameterized on character type, character traits,
and storage allocator
basic_string declared as:
template <class CharT,
class Traits = char_traits<CharT>,
class Allocator = allocator<CharT>>
class basic_string;
CharT: type of characters in string
Traits: class that describes certain properties of CharT (normally, use
default)
Allocator: type of object used to handle storage allocation (unless
custom storage allocator needed, use default)
string is simply abbreviation for basic_string<char>
what follows is only intended to provide overview of basic_string
template class (and string class)
for more details on basic_string, see:
http://www.cplusplus.com/reference/string/basic_string
http://en.cppreference.com/w/cpp/string/basic_string
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 541
Member Types
Member Type Description
traits_type Traits (i.e., character traits)
value_type Traits::char_type (i.e., character type)
allocator_type Allocator
size_type allocator_traits<Allocator>::size_type
difference_type allocator_traits<Allocator>::
difference_type
reference value_type&
const_reference const value_type&
pointer allocator_traits<Allocator>::pointer
const_pointer allocator_traits<Allocator>::
const_pointer
iterator random-access iterator type
const_iterator const random-access iterator type
reverse_iterator reverse iterator type
(reverse_iterator<iterator>)
const_reverse_iterator const reverse iterator type
(reverse_iterator<const_iterator>)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 542
Member Functions
Construction, Destruction, and Assignment
Member Name Description
constructor construct
destructor destroy
operator= assign
Iterators
Member Name Description
begin return iterator to beginning
end return iterator to end
cbegin return const iterator to beginning
cend return const iterator to end
rbegin return reverse iterator to reverse beginning
rend return reverse iterator to reverse end
crbegin return const reverse iterator to reverse beginning
crend return const reverse iterator to reverse end
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 543
Member Functions (Continued 1)
Capacity
Member Name Description
empty test if string empty
size get length of string
length same as size
max_size get maximum size of string
capacity get size of allocated storage
reserve change capacity
shrink_to_fit shrink to fit
Element Access
Member Name Description
operator[] access character in string (no bounds checking)
at access character in string (with bounds checking)
front access first character in string
back access last character in string
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 544
Member Functions (Continued 2)
Operations
Member Name Description
clear clear string
assign assign content to string
insert insert into string
push_back append character to string
operator+= append to string
append append to string
erase erase characters from string
pop_back delete last character from string
replace replace part of string
resize resize string
swap swap contents with another string
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 545
Member Functions (Continued 3)
Operations (Continued)
Member Name Description
c_str get nonmodifiable C-string equivalent
data obtain pointer to first character of string
copy copy sequence of characters from string
substr generate substring
compare compare strings
Search
Member Name Description
find find first occurrence of content in string
rfind find last occurrence of content in string
find_first_of find first occurrence of characters in string
find_first_not_of find first absence of characters in string
find_last_of find last occurrence of characters in string
find_last_not_of find last absence of characters in string
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 546
Member Functions (Continued 4)
Allocator
Member Name Description
get_allocator get allocator
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 547
Non-Member Functions
Numeric Conversions
Name Description
stoi convert string to int
stol convert string to long
stoll convert string to long long
stoul convert string to unsigned long
stoull convert string to unsigned long long
stof convert string to float
stod convert string to double
stold convert string to long double
to_string convert integral or floating-point value to string
to_wstring convert integral or floating-point value to wstring
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 548
string Example
1 #include <iostream>
2 #include <string>
3
4 int main() {
5 std::string s;
6 if (!(std::cin >> s)) {
7 s.clear();
8 }
9 std::cout << "string: " << s << ’\n’;
10 std::cout << "length: " << s.size() << ’\n’;
11 std::string b;
12 for (auto i = s.crbegin(); i != s.crend(); ++i) {
13 b.push_back(*i);
14 }
15 std::cout << "backwards: " << b << ’\n’;
16
17 std::string msg = "Hello";
18 msg += ", World!"; // append ", World!"
19 std::cout << msg << ’\n’;
20
21 const char* cstr = s.c_str();
22 std::cout << "C-style string: " << cstr << ’\n’;
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 549
Numeric/String Conversion Example
1 #include <iostream>
2 #include <string>
3
4 int main() {
5 double x = 42.24;
6 // Convert double to string.
7 std::string s = std::to_string(x);
8 std::cout << s << ’\n’;
9
10 s = "3.14";
11 // Convert string to double.
12 x = std::stod(s);
13 std::cout << x << ’\n’;
14
15 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 550
Section 2.8.5
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 551
The std::pair Class Template
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 552
pair Example
1 #include <tuple>
2 #include <cassert>
3
4 int main() {
5 std::pair p(true, 42);
6 assert(p.first && p.second == 42);
7 assert(p.first == std::get<0>(p) &&
8 p.second == std::get<1>(p));
9 std::pair q = std::make_pair(true, 42);
10 assert(p == q);
11 p = std::make_pair(false, 0);
12 assert(p != q);
13 p.swap(q);
14 auto [b, i] = p;
15 assert(b == true && i == 42);
16 assert(std::get<bool>(p) && std::get<0>(p));
17 assert(std::get<int>(p) == 42 &&
18 std::get<1>(p) == 42);
19 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 553
The std::tuple Class Template
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 554
tuple Example
1 #include <tuple>
2 #include <cassert>
3
4 int main() {
5 std::tuple t(true, 42, ’Z’);
6 auto u = std::make_tuple(true, 42, ’Z’);
7 assert(t == u);
8 assert(std::get<bool>(t) && std::get<0>(t));
9 assert(std::get<char>(t) == ’Z’ && std::get<2>(t) == ’Z’);
10 std::get<0>(t) = false;
11 assert(t != u);
12 std::tuple v(false, 0, ’0’);
13 u = std::make_tuple(true, 1, ’1’);
14 v.swap(u);
15 assert(std::get<0>(v));
16 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 555
The std::optional Class Template
simple container that manages optional value (i.e., value that may or may
not be present)
declaration:
template <class T> class optional;
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 556
optional Member Functions
Observers
Name Description
operator-> accesses contained value
operator* accesses contained value
operator bool tests if object contains value
has_value tests if object contains value
value returns contained value
value_or returns contained value if available and spec-
ified default value otherwise
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 557
optional Member Functions (Continued)
Modifiers
Name Description
swap exchange contents
reset clear any contained value
emplace constructs contained value in place
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 558
optional Example
1 #include <optional>
2 #include <string>
3 #include <exception>
4 #include <cassert>
5 #include <iostream>
6
7 int main() {
8 auto s = std::make_optional<std::string>("Hello!");
9 assert(s && s.has_value());
10 assert(s.value() == "Hello!");
11 auto t = std::make_optional<std::string>("Goodbye!");
12 s.swap(t);
13 assert(*s == "Goodbye!" && *t == "Hello!");
14 s.reset();
15 assert(!s && !s.has_value());
16 std::cout << s.value_or("Goodbye!") << ’\n’;
17 try {std::cout << s.value() << ’\n’;}
18 catch (const std::bad_optional_access&) {
19 std::cout << "caught exception\n";
20 }
21 s.emplace("Salut!");
22 std::cout << s.value() << ’\n’;
23
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 559
Example: Return Type of Function That Can Fail
1 #include <optional>
2 #include <string>
3 #include <fstream>
4 #include <iostream>
5
6 std::optional<std::string> read_file(const char* file_name) {
7 std::ifstream in(file_name);
8 std::optional<std::string> result;
9 result.emplace(std::istreambuf_iterator<char>(in),
10 std::istreambuf_iterator<char>());
11 if (in.fail() && !in.eof()) {
12 result.reset();
13 }
14 return result;
15 }
16
17 int main(int argc, char** argv) {
18 if (argc <= 1) {return 1;}
19 auto s = read_file(argv[1]);
20 if (!s) {
21 std::cerr << "unable to read file\n";
22 return 1;
23 }
24 std::cout << *s;
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 560
The std::variant Class Template
Observers
Name Description
index returns zero-based index of alternative held
by variant
valueless_by_exception tests if variant in invalid state
Modifiers
Name Description
emplace constructs value in variant in place
swap swaps value with another variant
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 562
variant Example
1 #include <variant>
2 #include <cassert>
3 #include <iostream>
4
5 int main() {
6 std::variant<int, double> x;
7 std::variant<int, double> y;
8 x = 2;
9 assert(std::get<int>(x) == std::get<0>(x));
10 assert(!x.valueless_by_exception());
11 y = 0.5;
12 assert(std::get<double>(y) == std::get<1>(y));
13 std::cout << std::get<int>(x) << ’\n’;
14 std::cout << std::get<double>(y) << ’\n’;
15 try {std::cout << std::get<double>(x) << ’\n’;}
16 catch (const std::bad_variant_access&) {
17 std::cout << "bad variant access\n";
18 }
19 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 563
The std::any Class
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 564
any Member Functions
Observers
Name Description
has_value tests if object holds value
type returns typeid of contained value
Modifiers
Name Description
emplace change contained object by constructing new
value in place
reset clear any contained object
swap swaps contents of two any objects
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 565
any Example
1 #include <any>
2 #include <cassert>
3 #include <string>
4 #include <iostream>
5
6 int main() {
7 std::any x{std::string("Hello")};
8 assert(x.has_value() && x.type() == typeid(std::string));
9 std::any y;
10 assert(!y.has_value());
11 x.swap(y);
12 assert(!x.has_value() && y.has_value());
13 x = y;
14 std::cout << std::any_cast<std::string>(x) << ’\n’;
15 y.reset();
16 assert(!y.has_value());
17 try {std::any_cast<int>(x);}
18 catch (const std::bad_any_cast&) {
19 std::cout << "any_cast failed\n";
20 }
21 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 566
Section 2.8.6
Time Measurement
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 567
Time Measurement
http://en.cppreference.com/w/cpp/chrono
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 568
std::chrono Types
Clocks
Name Description
system_clock system clock (which may be adjusted)
steady_clock monotonic clock that ticks at constant rate
high_resolution_clock clock with shortest tick period available
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 569
std::chrono Example: Measuring Elapsed Time
1 #include <iostream>
2 #include <chrono>
3 #include <cmath>
4
5 double get_result() {
6 double sum = 0.0;
7 for (long i = 0L; i < 1000000L; ++i) {
8 sum += std::sin(i) * std::cos(i);
9 }
10 return sum;
11 }
12
13 int main() {
14 // Get the start time.
15 auto start_time =
16 std::chrono::high_resolution_clock::now();
17 // Do some computation.
18 double result = get_result();
19 // Get the end time.
20 auto end_time = std::chrono::high_resolution_clock::now();
21 // Compute elapsed time in seconds.
22 double elapsed_time = std::chrono::duration<double>(
23 end_time - start_time).count();
24 // Print result and elapsed time.
25 std::cout << "result " << result << ’\n’;
26 std::cout << "time (in seconds) " << elapsed_time << ’\n’;
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 570
std::chrono Example: Determining Clock Resolution
1 #include <iostream>
2 #include <chrono>
3
4 // Get the granularity of a clock in seconds.
5 template <class C>
6 double granularity() {
7 return std::chrono::duration<double>(
8 typename C::duration(1)).count();
9 }
10
11 int main() {
12 std::cout << "system clock:\n" << "period "
13 << granularity<std::chrono::system_clock>() << ’\n’
14 << "steady "
15 << std::chrono::system_clock::is_steady << ’\n’;
16 std::cout << "high resolution clock:\n" << "period "
17 << granularity<std::chrono::high_resolution_clock>()
18 << ’\n’ << "steady "
19 << std::chrono::high_resolution_clock::is_steady << ’\n’;
20 std::cout << "steady clock:\n" << "period "
21 << granularity<std::chrono::steady_clock>() << ’\n’
22 << "steady "
23 << std::chrono::steady_clock::is_steady << ’\n’;
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 571
Section 2.8.7
Miscellany
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 572
The std::basic_string_view Class Template
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 573
std::basic_string_view Example
1 #include <string_view>
2 #include <string>
3 #include <iostream>
4 #include <cassert>
5
6 void output(std::string_view s) {
7 std::cout << s << ’\n’;
8 }
9
10 int main() {
11 std::string_view hello("hello");
12 assert(!hello.empty());
13 std::string_view he = hello.substr(0, 2);
14 assert(he.size() == 2);
15 assert(he[0] == ’h’ && he[1] == ’e’);
16 assert(hello.find("ell") == 1);
17 assert(hello.rfind("l") == 3);
18
19 std::string goodbye("goodbye");
20 std::string_view bye(goodbye);
21 bye.remove_prefix(4);
22 std::cout << bye << ’\n’;
23 std::string_view good(goodbye);
24 good.remove_suffix(3);
25 std::cout << good << ’\n’;
26 assert(goodbye.substr(4, 3) == bye);
27 output(bye);
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 574
Section 2.9
Miscellany
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 575
Name Lookup
Since C++ name lookup rules are quite complicated, we only present a
simplified (and therefore not fully correct) description of them here.
Qualified lookup. If the name A is preceded by the scope-resolution
operator, as in ::A or X::A, then use qualified name lookup.
In the first case, look in the global namespace for A. In the second case,
look up X, and then look inside it for A.
If X is a class and A is not a direct member, look in all of the direct bases of
X (and then each of their bases). If A is found in more than one base, fail.
Argument-dependent lookup. Otherwise, if the name is used as a
function call, such as A(X), use argument-dependent lookup.
Look for A in the namespace in which the type of X was declared, in the
friends of X, and if X is a template instantiation, similarly for each of the
arguments involved.
Unqualified lookup. Start with unqualified lookup if argument-dependent
lookup does not apply.
Start at the current scope and work outwards until the name is found.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 576
Argument-Dependent Lookup (ADL)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 577
ADL Example
1 #include <iostream>
2
3 namespace N {
4 class C { /* ... */ };
5 void f(C x) {std::cout << "N::f\n";}
6 void g(int x) {std::cout << "N::g\n";}
7 void h(C x) {std::cout << "N::h\n";}
8 }
9
10 struct D {
11 struct E {};
12 static void p(E e) {std::cout << "D::p\n";};
13 };
14
15 void h(N::C x) {std::cout << "::h\n";}
16
17 int main() {
18 N::C x;
19 f(x); // OK: calls N::f via ADL
20 N::f(x); // OK: calls N::f
21 g(42); // ERROR: g not found
22 N::g(42); // OK: calls N::g
23 h(x); // ERROR: ambiguous function call due to ADL
24 ::h(x); // OK: calls ::h
25 N::h(x); // OK: calls N::h
26 D::E e;
27 p(e); // ERROR: ADL only considers namespaces
28 D::p(e); // OK: calls D::p
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 578
ADL Example
1 #include <iostream>
2
3 namespace N {
4 struct W {};
5 void f(W x) {std::cout << "N::f\n";}
6 }
7
8 struct C {
9 void f(N::W x) {std::cout << "C::f\n";}
10 void g() {
11 N::W x;
12 f(x); // calls C::f (not N::f)
13 }
14 };
15
16 int main() {
17 C c;
18 c.g();
19 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 579
ADL Example
1 #include <iostream>
2 #include <string>
3
4 using namespace std::string_literals;
5
6 namespace N {
7 struct C {};
8 void f(int) {std::cout << "N::f\n";}
9 void g(C x) {std::cout << "N::g\n";}
10 void h(const std::string& x) {std::cout << "N::h\n";}
11 namespace M {
12 void f(int x) {std::cout << "N::M::f\n";}
13 // hides N::f
14 void g(int x) {std::cout << "N::M::g\n";}
15 // hides N::g
16 void h() {std::cout << "N::M::h\n";} // hides N::h
17 void u() {
18 N::C c;
19 f(42); // calls N::M::f (ADL looks nowhere)
20 g(c); // calls N::g via ADL (ADL looks in N)
21 h("hi"s); // ERROR: lookup finds N::M::h
22 // (ADL does not look in N)
23 }
24 }
25 }
26
27 int main() {N::M::u();}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 580
Swapping Values and ADL
If the type T provides its own swap function, the name lookup on swap will
yield this function through ADL.
Otherwise, the name lookup will find std::swap.
Thus, code like the above will result in a more efficient swap function
being used if available, with the std::swap function used as a fallback.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 581
Part 3
More C++
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 582
Section 3.1
Memory Management
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 583
Memory Management
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 584
Potential Problems Arising in Memory Management
leaked object: object created but not destroyed when no longer needed
leaked objects are problematic because can cause program to waste
memory or exhaust all available memory
premature deletion (a.k.a. dangling references): object is deleted when
one or more references to object still exist
premature deletion is problematic because, if object accessed after
deletion, results of doing so will be unpredictable (e.g., read garbage
value or overwrite other variables in program)
double deletion: object is deleted twice, invoking destructor twice
double deletion is problematic invoking destructor on nonexistent object is
unpredictable and furthermore double deletion can often corrupt data
structures used by memory allocator
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 585
Section 3.1.1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 586
Alignment
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 587
New Expressions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 589
New Expressions and Allocation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 590
Allocation Function Overload Resolution
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 591
Allocation Function Overload Resolution (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 592
New Expressions and Deallocation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 593
Delete Expressions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 595
Delete Expressions (Continued 2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 596
Operator New (i.e., operator new)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 598
Operator New Overloads (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 599
Operator New Examples
1 #include <new>
2 #include <cassert>
3
4 void func_1() {
5 // allocating operator new
6 int* ip = static_cast<int*>(::operator new(sizeof(int)));
7 assert(ip);
8 // ... (dellocate memory)
9 }
10
11 void func_2() {
12 // allocating and non-throwing operator new
13 int* ip = static_cast<int*>(::operator new(sizeof(int),
14 std::nothrow));
15 // ip may be null
16 // ... (deallocate memory)
17 }
18
19 void func_3() {
20 int i;
21 // non-allocating operator new
22 int* ip = static_cast<int*>(::operator new(sizeof(int),
23 static_cast<void*>(&i)));
24 assert(ip == &i);
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 600
Operator Array New (i.e., operator new[])
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 601
Operator Array New Overloads
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 602
Operator Array New Overloads (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 603
Operator Array New Examples
1 #include <new>
2 #include <cassert>
3
4 void func_1() {
5 // allocating operator array new
6 int* ip = static_cast<int*>(::operator new[](1000 * sizeof(int)));
7 assert(ip);
8 // ... (deallocate)
9 }
10
11 void func_2() {
12 int* ip = static_cast<int*>(::operator new[](1000 * sizeof(int),
13 std::nothrow));
14 // ip may be null
15 // ... (deallocate)
16 }
17
18 void func_3() {
19 static int a[1000];
20 int* ip = static_cast<int*>(::operator new[](1000 * sizeof(int),
21 static_cast<void*>(a)));
22 assert(ip == a);
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 604
Operator Delete (i.e., operator delete)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 605
Operator Delete Overloads
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 606
Operator Delete Examples
1 #include <new>
2 #include <cassert>
3
4 void func_1() {
5 // allocating operator new
6 int* ip = static_cast<int*>(::operator new(sizeof(int)));
7 assert(ip);
8 ::operator delete(ip);
9 }
10
11 void func_2() {
12 // allocating and non-throwing operator new
13 int* ip = static_cast<int*>(::operator new(sizeof(int),
14 std::nothrow));
15 // ip may be null
16 ::operator delete(ip);
17 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 607
Operator Array Delete (i.e., operator delete[])
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 608
Operator Array Delete Overloads
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 609
Operator Array Delete Examples
1 #include <new>
2 #include <cassert>
3
4 void func_1() {
5 // allocating operator array new
6 int* ip = static_cast<int*>(::operator new[](1000 * sizeof(int)));
7 assert(ip);
8 ::operator delete[](ip);
9 }
10
11 void func_2() {
12 int* ip = static_cast<int*>(::operator new[](1000 * sizeof(int),
13 std::nothrow));
14 // ip may be null
15 ::operator delete[](ip);
16 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 610
Replacing Operator New and Operator Delete
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 611
Example of Replacing Operator New and Operator Delete
1 #include <cstdio>
2 #include <cstdlib>
3 #include <new>
4
5 void* operator new(std::size_t size) {
6 auto ptr = std::malloc(size);
7 std::printf("operator new(%zu) returning %p\n", size, ptr);
8 return ptr;
9 }
10
11 void operator delete(void* ptr) noexcept {
12 std::printf("operator delete(%p)\n", ptr);
13 std::free(ptr);
14 }
15
16 void* operator new[](std::size_t size) {
17 auto ptr = std::malloc(size);
18 std::printf("operator new[](%zu) returning %p\n", size, ptr);
19 return ptr;
20 }
21
22 void operator delete[](void* ptr, std::size_t size) noexcept {
23 std::printf("operator delete[](%p)\n", ptr);
24 std::free(ptr);
25 }
26
27 int main() {
28 int* ip = new int;
29 delete ip;
30 int* ap = new int[10];
31 delete[] ap;
32 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 612
Motivation for Placement New
1 #include <cstdint>
2
3 // heap-allocated array of bounded size
4 template <class T>
5 class bvec {
6 public:
7 // create empty vector that can hold max_size elements
8 // why is this implementation extremely inefficient?
9 bvec(std::size_t max_size) {
10 start_ = new T[max_size];
11 end_ = start_ + max_size;
12 finish_ = start_; // mark array empty
13 }
14 // why is this implementation extremely inefficient?
15 ˜bvec() {
16 delete[] start_;
17 }
18 // ...
19 private:
20 T* start_; // start of storage for element data
21 T* finish_; // one past end of element data
22 T* end_; // end of storage for element data
23 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 613
Placement New
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 614
Placement New Examples
1 #include <new>
2 #include <vector>
3 #include <cassert>
4 #include <utility>
5
6 void func_1() {
7 int buffer;
8 int* ip = new (static_cast<void*>(&buffer)) int(42);
9 assert(ip == &buffer && buffer == 42);
10 }
11
12 void func_2() {
13 alignas(int) char buffer[sizeof(int)];
14 int* ip = new (static_cast<void*>(buffer)) int(42);
15 assert(static_cast<void*>(ip) == buffer && *ip == 42);
16 }
17
18 template <class T, class... Args>
19 T* construct_at(void* ptr, Args&&... args) {
20 return ::new (ptr) T(std::forward<Args>(args)...);
21 }
22
23 void func_3() {
24 alignas(std::vector<int>) char buffer[sizeof(std::vector<int>)];
25 std::vector<int>* vp = construct_at<std::vector<int>>(buffer, 1000, 42);
26 assert(static_cast<void*>(vp) == buffer && vp->size() == 1000 &&
27 (*vp)[0] == 42);
28 // ... (destroy vector)
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 615
Direct Destructor Invocation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 617
std::addressof Function Template
for memory management purposes, often necessary to obtain address of
object
if class overloads address-of operator, obtaining address of object
becomes more difficult
for convenience, standard library provides std::addressof function
template for querying address of object, which yields correct result even if
class overloads address-of operator
declaration:
template <class T>
constexpr T* addressof(T& arg) noexcept;
template <class T>
const T* addressof(const T&&) = delete;
addressof function should be used any time address of object is required
whose class may have overloaded address-of operator
example:
template <class T> foo(const T& x) {
const T* p = std::addressof(x);
// ...
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 618
std::addressof Example
1 #include <iostream>
2 #include <cassert>
3 #include <memory>
4
5 // class that overloads address-of operator
6 class Foo {
7 public:
8 Foo(int i) : i_(i) {}
9 const Foo* operator&() const {return nullptr;}
10 Foo* operator&() {return nullptr;}
11 int get() const {return i_;}
12 // ...
13 private:
14 int i_;
15 };
16
17 int main() {
18 Foo f(42);
19 assert(&f == nullptr);
20 assert(std::addressof(f) != nullptr &&
21 std::addressof(f)->get() == 42);
22 std::cout << std::addressof(f) << ’\n’;
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 619
The std::aligned_storage Class Template
often need can arise for buffer of particular size and alignment
for convenience, standard library provides std::aligned_storage class
template for specifying such buffers
declaration:
template <std::size_t Size, std::size_t Align =
__default_alignment> struct aligned_storage;
Size is size of storage buffer in bytes
Align is alignment of storage buffer (which has
implementation-dependent default)
for additional convenience, std::aligned_storage_t alias template
also provided
declaration:
template <std::size_t Size, std::size_t Align =
__default_alignment> using aligned_storage_t = typename
aligned_storage<Len, Align>::type;
example:
std::aligned_storage_t<sizeof(std::string),
alignof(std::string)> buffer;
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 620
Optional Value Example
consider container class called optval that can hold optional value
class templated on type T of optional value
container object in one of two states:
1 holding value of type T
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 621
Optional Value Example: optval.hpp
1 #include <new>
2 #include <type_traits>
3
4 template <class T> class optval {
5 public:
6 optval() : valid_(false) {}
7 ˜optval() {clear();}
8 optval(const optval&) = delete; // for simplicity
9 optval& operator=(const optval&) = delete; // for simplicity
10 bool has_value() const noexcept {return valid_;}
11 const T& get() const {return reinterpret_cast<const T&>(storage_);}
12 void clear() noexcept {
13 if (valid_) {
14 reinterpret_cast<T*>(&storage_)->˜T();
15 valid_ = false;
16 }
17 }
18 void set(const T& value) {
19 clear();
20 ::new (static_cast<void*>(&storage_)) T(value);
21 valid_ = true;
22 }
23 private:
24 bool valid_; // is value valid?
25 std::aligned_storage_t<sizeof(T), alignof(T)> storage_;
26 // storage for value
27 // or alternatively: alignas(T) char storage_[sizeof(T)];
28 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 622
Optional Value Example: User Code
1 #include <cassert>
2 #include <string>
3 #include <iostream>
4 #include "optional_1_util.hpp"
5
6 int main() {
7 optval<std::string> s;
8 assert(!s.has_value());
9 s.set("Hello, World");
10 assert(s.has_value());
11 std::cout << s.get() << ’\n’;
12 s.clear();
13 assert(!s.has_value());
14 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 623
Handling Uninitialized Storage
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 624
Functions for Uninitialized Storage
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 625
Functions for Uninitialized Storage (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 626
Some Example Implementations
1 template<class InputIter, class ForwardIter>
2 ForwardIter uninitialized_copy(InputIter first, InputIter last,
3 ForwardIter result) {
4 using Value = typename std::iterator_traits<ForwardIter>::value_type;
5 ForwardIter current = result;
6 try {
7 for (; first != last; ++first, (void) ++current) {
8 ::new (static_cast<void*>(std::addressof(*current))) Value(*first);
9 }
10 } catch (...) {
11 for (; result != current; ++result) {
12 result->˜Value();
13 }
14 throw;
15 }
16 return current;
17 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 627
Bounded Array Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 628
Bounded Array Example: aligned_buffer.hpp
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 629
Bounded Array Example: array.hpp (1)
1 #include <memory>
2 #include <algorithm>
3 #include <type_traits>
4 #include "aligned_buffer.hpp"
5
6 template <class T, std::size_t N> class array {
7 public:
8 array() : finish_(buf_.start()) {}
9 array(const array& other);
10 array(array&& other);
11 ˜array() {clear();}
12 array& operator=(const array& other);
13 array& operator=(array&& other);
14 explicit array(std::size_t size);
15 array(std::size_t size, const T& value);
16 constexpr std::size_t max_size() const noexcept {return N;}
17 std::size_t size() const noexcept {return finish_ - buf_.start();}
18 T& operator[](std::size_t i) {return buf_.start()[i];}
19 const T& operator[](std::size_t i) const {return buf_.start()[i];}
20 T& back() {return finish_[-1];}
21 const T& back() const {return finish_[-1];}
22 void push_back(const T& value);
23 void pop_back();
24 void clear() noexcept;
25 private:
26 T* finish_; // one past last element in buffer
27 aligned_buffer<T, N> buf_; // buffer for array elements
28 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 630
Bounded Array Example: array.hpp (2)
30 template <class T, std::size_t N>
31 array<T, N>::array(const array& other) {
32 finish_ = std::uninitialized_copy(other.buf_.start(),
33 const_cast<const T*>(other.finish_), buf_.start());
34 }
35
36 template <class T, std::size_t N>
37 array<T, N>::array(array&& other) {
38 finish_ = std::uninitialized_move(other.buf_.start(), other.finish_,
39 buf_.start());
40 }
41
42 template <class T, std::size_t N>
43 array<T, N>& array<T, N>::operator=(const array& other) {
44 if (this != &other) {
45 clear();
46 finish_ = std::uninitialized_copy(other.buf_.start(),
47 const_cast<const T*>(other.finish_), buf_.start());
48 }
49 return *this;
50 }
51
52 template <class T, std::size_t N>
53 array<T, N>& array<T, N>::operator=(array&& other) {
54 if (this != &other) {
55 clear();
56 finish_ = std::uninitialized_move(other.buf_.start(), other.finish_,
57 buf_.start());
58 }
59 return *this;
60 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 631
Bounded Array Example: array.hpp (3)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 632
Bounded Array Example: array.hpp (4)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 633
Vector Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 634
Vector Example: vec.hpp (1)
1 #include <new>
2 #include <algorithm>
3 #include <type_traits>
4 #include <memory>
5
6 template <class T> class vec {
7 public:
8 vec() : start_(nullptr), finish_(nullptr), end_(nullptr) {}
9 vec(const vec& other);
10 vec(vec&& other) noexcept;
11 ˜vec();
12 vec& operator=(const vec& other);
13 vec& operator=(vec&& other) noexcept;
14 explicit vec(std::size_t size);
15 vec(std::size_t n, const T& value);
16 std::size_t capacity() const noexcept {return end_ - start_;}
17 std::size_t size() const noexcept {return finish_ - start_;}
18 T& operator[](int i) {return start_[i];}
19 const T& operator[](int i) const {return start_[i];}
20 T& back() {return finish_[-1];}
21 const T& back() const {return finish_[-1];}
22 void push_back(const T& value);
23 void pop_back();
24 void clear() noexcept;
25 private:
26 void grow(std::size_t n);
27 T* start_; // start of element storage
28 T* finish_; // one past last valid element
29 T* end_; // end of element storage
30 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 635
Vector Example: vec.hpp (2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 636
Vector Example: vec.hpp (3)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 637
Vector Example: vec.hpp (4)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 638
Vector Example: vec.hpp (5)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 639
Vector Example: vec.hpp (6)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 640
Section 3.1.3
Allocators
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 641
Allocators
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 642
Containers, Allocators, and the Default Allocator
unordered_multimap
all container class templates in standard library that take allocator as
parameter use default of std::allocator<T> where T must be type of
element held by container
std::allocator employs operator new and operator delete for memory
allocation
in many contexts, default allocator is quite adequate
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 643
Application Use of Allocator
1 #include <memory>
2 #include <vector>
3 #include <map>
4 #include <cassert>
5 #include <boost/pool/pool_alloc.hpp>
6
7 int main() {
8 // use default allocator
9 std::vector<int> u;
10 u.push_back(42);
11
12 // explicitly specify default allocator
13 std::vector<int, std::allocator<int>> v;
14 static_assert(std::is_same_v<decltype(u), decltype(v)>);
15 assert(u.get_allocator() == v.get_allocator());
16 v.push_back(42);
17
18 // specify an allocator type from Boost
19 std::vector<int, boost::pool_allocator<int>> w;
20 w.push_back(42);
21
22 // explicitly specify default allocator
23 std::map<int, long, std::less<int>,
24 std::allocator<std::pair<const int, long>>> x;
25 x.insert({1, 2});
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 644
Why Not Just Always Use the Default Allocator?
custom allocators used when greater control is needed over how memory
is managed
often this greater control is desired for:
improved efficiency (e.g., better locality and less contention)
debugging
performance analysis (e.g., collecting statistics on memory allocation)
testing (e.g., forcing allocation failures)
security (e.g., locking and clearing memory)
since many allocation strategies are possible, one strategy cannot be best
in all situations
some allocation strategies include:
stack-based allocation
per-container allocation
per-thread allocation (which avoids synchronization issues)
pooled allocation
arena allocation
may want to handle relocatable data (e.g., shared memory)
may want to use memory mapped files
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 645
Examples of Allocators
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 646
Allocators
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 647
Allocator Members
allocator type for objects of (cv-unqualified) type T
many members are optional, with std::allocator_traits class
effectively providing defaults for omitted members
value_type:
type T of object for which allocator manages (i.e., allocates and
deallocates) memory
pointer:
pointer type used to refer to storage obtained from allocator (not
necessarily T*)
optional: default of T* provided by allocator_traits
const_pointer:
const version of pointer
optional: default of const T* provided by allocator_traits
pointer allocate(size_type n):
allocate storage suitable for n objects of type T
void deallocate(pointer ptr, size_type n):
deallocates storage pointed to by ptr, where ptr must have been obtained
by previous call to allocate and n must match value given in that call
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 648
Allocator Members (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 649
Remarks on Allocators
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 650
Malloc-Based Allocator: Allocator Code
1 #include <cstdlib>
2 #include <new>
3
4 template <class T>
5 struct mallocator {
6 using value_type = T;
7 mallocator() noexcept {};
8 template <class U> mallocator(const mallocator<U>&) noexcept {}
9 T* allocate(std::size_t n) const;
10 void deallocate(T* p, std::size_t n) const noexcept;
11 template <class U> bool operator==(const mallocator<U>&)
12 const noexcept {return true;}
13 template <class U> bool operator!=(const mallocator<U>&)
14 const noexcept {return false;}
15 };
16
17 template <class T>
18 T* mallocator<T>::allocate(std::size_t n) const {
19 if (!n) {return nullptr;}
20 if (n > static_cast<std::size_t>(-1) / sizeof(T))
21 {throw std::bad_array_new_length();}
22 void* p = std::malloc(n * sizeof(T));
23 if (!p) {throw std::bad_alloc();}
24 return static_cast<T*>(p);
25 }
26
27 template <class T>
28 void mallocator<T>::deallocate(T* p, std::size_t) const noexcept
29 {std::free(p);}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 651
Malloc-Based Allocator: User Code
1 #include "mallocator.hpp"
2 #include <cassert>
3 #include <vector>
4 #include <type_traits>
5
6 int main() {
7 std::vector<int, mallocator<int>> v;
8 // uses mallocator<int> for memory allocation
9 std::vector<int> w;
10 // or equivalently, std::vector<int, std::allocator<int>>
11 // uses std::allocator<int> for memory allocation
12 static_assert(!std::is_same_v<decltype(v)::allocator_type,
13 decltype(w)::allocator_type>);
14 for (int i = 0; i < 128; ++i) {
15 v.push_back(42);
16 w.push_back(42);
17 }
18 std::vector<int, mallocator<int>> x;
19 assert(v.get_allocator() == x.get_allocator());
20 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 652
Allocator Propagation
in certain contexts, must consider if and how allocators should be
propagated between container objects
lateral propagation refers to propagation of allocator when copying,
moving, and swapping containers:
when container copy/move constructed, what allocator does new container
receive?
when container copy/move assigned, what allocator does
copied-to/moved-to container receive?
when containers swapped, what allocator does each container receive?
deep propagation refers to propagation of allocator from parent container
to its descendents in hierarchy of nested containers:
if container contains types which themselves require allocators, how can
contained elements be made aware of container’s allocator so that
compatible allocator can be used?
each allocator has its own lateral propagation properties, which can be
accessed via std::allocator_traits
deep allocator propagation can be controlled via
std::scoped_allocator_adaptor
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 653
New-Based Allocator
1 #include <new>
2 #include <type_traits>
3
4 template <class T>
5 struct allocator {
6 using value_type = T;
7 using propagate_on_container_move_assignment = std::true_type;
8 using is_always_equal = std::true_type;
9 allocator() noexcept {};
10 allocator(const allocator&) noexcept {};
11 template <class U> allocator(const allocator<U>&) noexcept {}
12 ˜allocator() {}
13 T* allocate(std::size_t n);
14 void deallocate(T* p, std::size_t n) const noexcept
15 {::operator delete(p);}
16 };
17
18 template <class T>
19 T* allocator<T>::allocate(std::size_t n) {
20 if (n > static_cast<std::size_t>(-1) / sizeof(T))
21 {throw std::bad_array_new_length();}
22 return static_cast<T*>(::operator new(n * sizeof(T)));
23 }
24
25 template <class T, class U>
26 inline bool operator==(const allocator<T>&, const allocator<U>&) noexcept
27 {return true;}
28
29 template <class T, class U>
30 inline bool operator!=(const allocator<T>&, const allocator<U>&) noexcept
31 {return false;}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 654
Fixed-Size Arena Allocator: Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 655
Fixed-Size Arena Allocator: Code (1)
1 #include <memory>
2 #include <cstddef>
3 #include <new>
4
5 template <std::size_t N, std::size_t Align = alignof(std::max_align_t)>
6 class arena {
7 public:
8 arena() : ptr_(buf_) {}
9 arena(const arena&) = delete;
10 arena& operator=(const arena&) = delete;
11 ˜arena() = default;
12 constexpr std::size_t alignment() const {return Align;}
13 constexpr std::size_t capacity() const {return N;}
14 constexpr std::size_t used() const {return ptr_ - buf_;}
15 constexpr std::size_t free() const {return N - used();}
16 template <std::size_t ReqAlign> void* allocate(std::size_t n);
17 void deallocate(void* ptr, std::size_t n) {}
18 void clear() {ptr_ = buf_;}
19 private:
20 template <std::size_t ReqAlign>
21 static char* align(char* ptr, std::size_t n, std::size_t max);
22 alignas(Align) char buf_[N]; // storage buffer
23 char* ptr_; // pointer to first unused byte
24 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 656
Fixed-Size Arena Allocator: Code (2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 657
Fixed-Size Arena Allocator: Code (3)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 658
Fixed-Size Arena Allocator: Code (4)
69 private:
70 template <class T1, std::size_t N1, std::size_t A1, class T2,
71 std::size_t N2, std::size_t A2>
72 friend bool operator==(const salloc<T1, N1, A1>&,
73 const salloc<T2, N2, A2>&);
74 template <class, std::size_t, std::size_t> friend class salloc;
75 arena_type* a_; // arena from which to allocate storage
76 };
77
78 template <class T1, std::size_t N1, std::size_t A1, class T2, std::size_t N2,
79 std::size_t A2>
80 inline bool operator==(const salloc<T1, N1, A1>& a,
81 const salloc<T2, N2, A2>& b)
82 {return N1 == N2 && A1 == A2 && a.a_ == b.a_;}
83
84 template <class T1, std::size_t N1, std::size_t A1, class T2, std::size_t N2,
85 std::size_t A2>
86 inline bool operator!=(const salloc<T1, N1, A1>& a,
87 const salloc<T2, N2, A2>& b)
88 {return !(a == b);}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 659
Fixed-Size Arena Allocator: User Code
1 #include <vector>
2 #include <list>
3 #include <iostream>
4 #include "salloc.hpp"
5
6 int main() {
7 using alloc = salloc<int, 1024, sizeof(int)>;
8 alloc::arena_type a;
9 std::vector<int, alloc> v{{0, 1, 2, 3}, a};
10 std::vector<int, alloc> w{{0, 2, 4, 6}, a};
11 std::list<int, alloc> p{{1, 3, 5, 7}, a};
12 std::cout << a.free() << ’\n’;
13 v.push_back(42);
14 for (auto&& i : v) {std::cout << i << ’\n’;}
15 for (auto&& i : w) {std::cout << i << ’\n’;}
16 for (auto&& i : p) {std::cout << i << ’\n’;}
17 std::cout << a.free() << ’\n’;
18
19 // std::vector<int, alloc> x(1024);
20 // std::list<int, alloc> y;
21 // ERROR: allocator cannot be default constructed
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 660
Allocator-Aware Containers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 661
The std::allocator_traits Class Template
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 662
Lateral Allocator Propagation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 664
Optional Value Example
consider container class template called optval that can hold optional
value
class templated on element type T and allocator type
container object in one of two states:
1 holding value of type T
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 665
Optional Value Example: Code (1)
1 #include <memory>
2 #include <type_traits>
3 #include <utility>
4
5 template <class T, class Alloc = std::allocator<T>>
6 class optval : private Alloc {
7 public:
8 using value_type = T;
9 using allocator_type = Alloc;
10 private:
11 using traits = typename std::allocator_traits<Alloc>;
12 public:
13 using pointer = typename traits::pointer;
14 using const_pointer = typename traits::const_pointer;
15 optval(std::allocator_arg_t, const allocator_type& alloc) :
16 Alloc(alloc), value_(nullptr) {}
17 optval() : optval(std::allocator_arg, allocator_type()) {}
18 optval(std::allocator_arg_t, const allocator_type& alloc,
19 const optval& other);
20 optval(const optval& other);
21 optval(std::allocator_arg_t, const allocator_type& alloc, optval&& other)
22 noexcept;
23 optval(optval&& other) noexcept;
24 optval(std::allocator_arg_t, const allocator_type& alloc, const T& value);
25 optval(const T& value);
26 ˜optval();
27 optval& operator=(const optval& other);
28 optval& operator=(optval&& other)
29 noexcept(traits::propagate_on_container_move_assignment::value);
30 void swap(optval& other) noexcept;
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 666
Optional Value Example: Code (2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 667
Optional Value Example: Code (3)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 668
Optional Value Example: Code (4)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 669
Optional Value Example: Code (5)
97 template <class T, class Alloc>
98 auto optval<T, Alloc>::operator=(optval&& other)
99 noexcept(traits::propagate_on_container_move_assignment::value) -> optval& {
100 if (this != &other) {
101 if constexpr (traits::propagate_on_container_move_assignment::value) {
102 clear();
103 std::swap(alloc_(), other.alloc_());
104 std::swap(value_, other.value_);
105 } else if (alloc_() == other.alloc_()) {
106 clear();
107 std::swap(value_, other.value_);
108 } else {
109 pointer p = copy_(alloc_(), other.value_);
110 std::swap(value_, other.value_);
111 other.clear();
112 value_ = p;
113 }
114 }
115 return *this;
116 }
117
118 template <class T, class Alloc>
119 void optval<T, Alloc>::swap(optval& other) noexcept {
120 // require POCS to be true or allocators equivalent
121 assert(traits::propagate_on_container_swap::value ||
122 alloc_() == other.alloc_());
123 if constexpr (traits::propagate_on_container_swap::value)
124 {std::swap(alloc_(), other.alloc_());}
125 std::swap(value_, other.value_);
126 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 670
Optional Value Example: Code (6)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 671
The std::scoped_allocator_adaptor Class Template
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 673
scoped_allocator_adaptor Example
1 #include <vector>
2 #include <scoped_allocator>
3 #include <boost/interprocess/managed_shared_memory.hpp>
4 #include <boost/interprocess/allocators/adaptive_pool.hpp>
5
6 namespace bi = boost::interprocess;
7
8 template <class T>
9 using alloc = typename bi::adaptive_pool<T, typename
10 bi::managed_shared_memory::segment_manager>;
11
12 int main () {
13 using row = std::vector<int, alloc<int>>;
14 using matrix = std::vector<row,
15 std::scoped_allocator_adaptor<alloc<row>>>;
16 bi::managed_shared_memory s(bi::create_only, "data", 8192);
17 matrix v(s.get_segment_manager());
18 v.resize(4);
19 for (int i = 0; i < 4; ++i) {v[i].push_back(0);}
20 bi::shared_memory_object::remove("data");
21 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 674
Section 3.1.4
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 675
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 676
Talks I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 677
Talks II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 678
Talks III
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 679
Section 3.2
Smart Pointers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 680
Section 3.2.1
Introduction
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 681
Memory Management, Ownership, and Raw Pointers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 682
Smart Pointers
smart pointer is object that has interface similar to raw pointer (e.g.,
provides operations such as indirection/dereferencing and assignment)
but offers some additional functionality
smart pointers provide RAII mechanism for managing memory resource
(i.e., pointed-to memory)
unlike raw pointer, smart pointer owns its pointed-to memory
consequently, smart pointer must provide mechanism for deallocating
pointed-to memory when no longer needed
some smart-pointer types allow only exclusive ownership, while others
allow shared ownership
destructor for smart pointer releases memory to which pointer refers if no
longer needed (i.e., no other owners remain)
smart pointers play crucial role in writing exception-safe code
smart pointers should always be used (instead of raw pointers) when
ownership of piece of memory needs to be tracked (e.g., so that it can be
deallocated when no longer needed)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 683
Section 3.2.2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 684
The std::unique_ptr Template Class
std::unique_ptr is smart pointer that retains exclusive ownership of
object through pointer
declaration:
template <class T, class Deleter = std::default_delete<T>>
class unique_ptr;
reasonable implementation would have zero memory cost for deleter state
in case of:
default deleter
deleter of functor/closure type with no state
if no memory cost for deleter state, unique_ptr has same memory cost
as raw pointer
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 686
std::unique_ptr Member Functions
Construction, Destruction, and Assignment
Member Name Description
constructor constructs new unique_ptr
destructor destroys managed object (if any)
operator= assigns unique_ptr
Modifiers
Member Name Description
release returns pointer to managed object and releases ownership
reset replaces managed object
swap swaps managed objects
Observers
Member Name Description
get returns pointer to managed object
get_deleter returns deleter used for destruction of managed object
operator bool check if there is associated managed object
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 687
std::unique_ptr Member Functions (Continued)
Dereferencing/Subscripting
Member Name Description
operator* dereferences pointer to managed object
operator-> dereferences pointer to managed object
operator[] provides indexed access to managed array
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 688
std::unique_ptr Example
1 #include <memory>
2 #include <cassert>
3
4 void func() {
5 auto p1(std::make_unique<int>(42));
6 assert(*p1 == 42);
7
8 // std::unique_ptr<int> p3(p1); // ERROR: not copyable
9 // p3 = p1; // ERROR: not copyable
10
11 std::unique_ptr<int> p2(std::move(p1)); // OK: movable
12 // Transfers ownership from p1 to p2, invalidating p1.
13 assert(p1.get() == nullptr && *p2 == 42);
14
15 p1 = std::move(p2); // OK: movable
16 // Transfers ownership from p2 to p1, invalidating p2.
17 assert(p2.get() == nullptr && *p1 == 42);
18
19 p1.reset();
20 // Invalidates p1.
21 assert(p1.get() == nullptr);
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 689
std::unique_ptr Example
1 #include <memory>
2 #include <cassert>
3
4 int main() {
5 auto p0 = std::make_unique<int>(0);
6 assert(*p0 == 0);
7 int* r0 = p0.get();
8 auto p1 = std::make_unique<int>(1);
9 assert(*p1 == 1);
10 auto r1 = p1.get();
11 p0.swap(p1);
12 assert(p0.get() == r1 && p1.get() == r0);
13 p1.swap(p0);
14 assert(p0.get() == r0 && p1.get() == r1);
15 p1.reset();
16 assert(p1.get() == nullptr);
17 assert(!p1);
18 int* ip = p1.release();
19 assert(!p1);
20 // ... Do not throw exceptions here.
21 delete ip;
22 p1.reset(new int(42));
23 assert(*p1 == 42);
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 690
Example: std::unique_ptr with Custom Deleter
1 #include <memory>
2 #include <iostream>
3 #include <cstring>
4 #include <cstdlib>
5
6 using up = std::unique_ptr<char[], void(*)(char*)>;
7
8 char *allocate(std::size_t n) {
9 return static_cast<char*>(std::malloc(n));
10 }
11
12 void deallocate(char* p) {
13 std::cout << "deallocate called\n";
14 std::free(p);
15 }
16
17 up string_duplicate(const char *s) {
18 std::size_t len = std::strlen(s);
19 up result(allocate(len + 1), deallocate);
20 std::strcpy(result.get(), s);
21 return result;
22 }
23
24 int main() {
25 auto p = string_duplicate("Hello, World!");
26 std::cout << p.get() << ’\n’;
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 691
Section 3.2.3
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 692
The std::shared_ptr Template Class
std::shared_ptr is smart pointer that retains shared ownership of
object through pointer
declaration:
template <class T> class shared_ptr;
T is type of object to be managed (i.e., owned object)
multiple shared_ptr objects may own same object
owned object is deleted when last remaining owning shared_ptr object
is destroyed, assigned another pointer via assignment, or reset via reset
shared_ptr object is movable, where move transfers ownership
shared_ptr object is copyable, where copy creates additional owner
thread safety guaranteed for shared_ptr object itself but not owned
object
std::make_shared (and std::allocate_shared) often used to create
shared_ptr objects (for both efficiency and exception-safety reasons)
shared_ptr has more overhead than unique_ptr so unique_ptr
should be preferred unless shared ownership required
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 693
The std::shared_ptr Template Class (Continued)
shared_ptr<T>
Pointer to T Object
Pointer to Control Block Control Block Managed Object
..
Pointer to Managed Object .
Use Count T Object
shared_ptr<T> ..
Weak Count .
Pointer to T Object
Other Data
Pointer to Control Block
shared_ptr<T>
Pointer to T Object
Pointer to Control Block
Control Block
Pointer to Managed Object Managed Object
..
Use Count (1) .
Weak Count (1) T Object
..
Other Data .
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 695
std::shared_ptr Reference Counting Example (Continued 1)
shared_ptr<T>
Pointer to T Object
Pointer to Control Block
shared_ptr<T>
Pointer to T Object
Pointer to Control Block
Control Block
Pointer to Managed Object Managed Object
..
Use Count (2) .
Weak Count (1) T Object
..
Other Data .
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 696
std::shared_ptr Reference Counting Example (Continued 2)
shared_ptr<T>
Pointer to T Object
Pointer to Control Block
shared_ptr<T>
Pointer to T Object
Pointer to Control Block
shared_ptr<T>
Pointer to T Object
Pointer to Control Block
Control Block
Pointer to Managed Object Managed Object
..
Use Count (3) .
Weak Count (1) T Object
..
Other Data .
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 697
std::shared_ptr Member Functions
Modifiers
Member Name Description
reset replaced managed object
swap swaps managed objects
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 698
std::shared_ptr Member Functions (Continued)
Observers
Member Name Description
get returns pointer to managed object
use_count returns number of shared_ptr objects referring to
same managed object
unique checks if managed object is managed only by current
shared_ptr instance
operator bool checks if there is associated managed object
owner_before provide owner-based ordering of shared pointers
Dereferencing/Subscripting
Member Name Description
operator* dereference pointer to managed object
operator-> dereference pointer to managed object
operator[] provide indexed access to managed array
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 699
std::shared_ptr Example
1 #include <memory>
2 #include <cassert>
3
4 int main() {
5 auto p1(std::make_shared<int>(0));
6 assert(*p1 == 0 && p1.use_count() == 1 && p1.unique());
7
8 std::shared_ptr<int> p2(p1);
9 assert(*p2 == 0 && p2.use_count() == 2 && !p2.unique());
10
11 *p2 = 42;
12 assert(*p1 == 42);
13
14 p2.reset();
15 assert(!p2);
16 assert(*p1 == 42 && p1.use_count() == 1 && p1.unique());
17
18 int* ip = p1.get();
19 assert(*ip == 42);;
20
21 ip = p2.get();
22 assert(ip == nullptr);
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 700
std::shared_ptr and const
1 #include <memory>
2 #include <iostream>
3 #include <string>
4
5 int main() {
6 std::shared_ptr<std::string> s =
7 std::make_shared<std::string>("hello");
8
9 std::shared_ptr<const std::string> cs = s;
10
11 *s = "goodbye";
12
13 // *cs = "bonjour"; // ERROR: const
14
15 std::cout << *cs.get() << ’\n’;
16 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 701
Example: Shared Pointer to Subobject of Managed Object
1 #include <memory>
2 #include <vector>
3 #include <cassert>
4 #include <iostream>
5
6 struct Widget {
7 Widget(int i_, const std::vector<int>& v_) :
8 i(i_), v(v_) {}
9 ˜Widget() {std::cout << "destructor called\n";}
10 int i;
11 std::vector<int> v;
12 };
13
14 int main() {
15 auto wp(std::make_shared<Widget>(42,
16 std::vector<int>{1, 2, 3}));
17 assert(wp.use_count() == 1);
18 assert(wp->i == 42 && wp->v.size() == 3);
19 std::shared_ptr<std::vector<int>> vp(wp, &wp->v);
20 assert(wp.use_count() == 2 && vp.use_count() == 2);
21 assert(vp->size() == 3);
22 wp = nullptr; // equivalently: wp.reset();
23 // managed Widget object not destroyed
24 assert(vp.use_count() == 1 && vp->size() == 3);
25 vp = nullptr; // equivalently: vp.reset();
26 // managed Widget object destroyed
27 // ...
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 702
Example: Shared Pointer to Subobject of Managed Object
(Continued 1)
wp
p v
cb i
p
rc = 1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 703
Example: Shared Pointer to Subobject of Managed Object
(Continued 2)
wp vp
p v p
cb i cb
p
rc = 2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 704
The std::enable_shared_from_this Class Template
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 705
enable_shared_from_this Example
1 #include <memory>
2 #include <cassert>
3
4 // Aside: This is an example of the CRTP.
5 class Widget : public std::enable_shared_from_this<Widget>
6 {
7 public:
8 std::shared_ptr<Widget> getSharedPtr() {
9 return shared_from_this();
10 }
11 std::shared_ptr<const Widget> getSharedPtr() const {
12 return shared_from_this();
13 }
14 // ...
15 };
16
17 int main() {
18 std::shared_ptr<Widget> a(new Widget);
19 std::shared_ptr<Widget> b = a->getSharedPtr();
20 assert(b == a);
21 std::shared_ptr<const Widget> c = a->getSharedPtr();
22 assert(c == a);
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 706
Example: std::shared_ptr
1 #include <memory>
2 #include <array>
3 #include <string>
4 #include <iostream>
5
6 using namespace std::literals;
7
8 int main() {
9 std::array<std::shared_ptr<const std::string>, 3> all = {
10 std::make_shared<const std::string>("apple"s),
11 std::make_shared<const std::string>("orange"s),
12 std::make_shared<const std::string>("banana"s)
13 };
14 std::array<std::shared_ptr<const std::string>, 2> some =
15 {all[0], all[1]};
16
17 for (auto& x : all) {
18 std::cout << *x << ’ ’ << x.use_count() << ’\n’;
19 }
20 }
21
22 /* output:
23 apple 2
24 orange 2
25 banana 1
26 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 707
Example: std::shared_ptr (Continued)
apple
p
some
rc = 2
all .. p
.
p cb
cb p
orange
p cb
cb p
rc = 2
p ..
.
cb
banana
p
rc = 1
..
.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 708
Section 3.2.4
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 709
std::shared_ptr and Circular References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 710
Circular Reference Example
1 #include <memory>
2 #include <iostream>
3 #include <cassert>
4
5 struct Node {
6 Node(int id_) : id(id_) {}
7 ˜Node() {std::cout << "destroying node " << id << ’\n’;}
8 std::shared_ptr<Node> parent;
9 std::shared_ptr<Node> left;
10 std::shared_ptr<Node> right;
11 int id;
12 };
13
14 void func() {
15 std::shared_ptr<Node> root(std::make_shared<Node>(1));
16 assert(root.use_count() == 1);
17 root->left = std::make_shared<Node>(2);
18 assert(root.use_count() == 1 &&
19 root->left.use_count() == 1);
20 root->left->parent = root;
21 assert(root.use_count() == 2 &&
22 root->left.use_count() == 1);
23 // When root is destroyed, the reference count for each
24 // of the managed Node objects does not reach zero, and
25 // no Node object is destroyed.
26 // Node::˜Node is not called here
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 711
Circular Reference Example (Continued 1)
root
p
p p
cb
cb
l p
cb
r p
cb
i = 1
p
rc = 1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 712
Circular Reference Example (Continued 2)
root
p
p p p p
cb
cb cb
l p l p
cb cb
r p r p
cb cb
i = 1 i = 2
p p
rc = 1 rc = 1
create new node, making it left child of root node (parent link not set)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 713
Circular Reference Example (Continued 3)
root
p
p p p p
cb
cb cb
l p l p
cb cb
r p r p
cb cb
i = 1 i = 2
p p
rc = 2 rc = 1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 714
Circular Reference Example (Continued 4)
p p p p
cb cb
l p l p
cb cb
r p r p
cb cb
i = 1 i = 2
p p
rc = 1 rc = 1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 715
The std::weak_ptr Template Class
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 716
std::weak_ptr Member Functions
Modifiers
Member Name Description
reset replaced managed object
swap swaps managed objects
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 717
std::weak_ptr Member Functions (Continued)
Observers
Member Name Description
use_count returns number of shared_ptr objects referring to
same managed object
expired checks if referenced object was already deleted
lock creates shared_ptr that manages referenced object
owner_before provide owner-based ordering of weak pointers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 718
Typical shared_ptr/weak_ptr Implementation
shared_ptr<T> or weak_ptr<T>
Pointer to T Object
Pointer to Control Block Control Block Managed Object
..
Pointer to Managed Object .
Use Count T Object
..
Weak Count .
Other Data
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 720
std::weak_ptr Example
1 #include <memory>
2 #include <iostream>
3 #include <cassert>
4
5 void func(std::weak_ptr<int> wp) {
6 auto sp = wp.lock();
7 if (sp) {
8 std::cout << *sp << ’\n’;
9 } else {
10 std::cout << "expired\n";
11 }
12 }
13
14 int main() {
15 std::weak_ptr<int> wp;
16 {
17 auto sp = std::make_shared<int>(42);
18 wp = sp;
19 assert(wp.use_count() == 1 && wp.expired() == false);
20 func(wp);
21 // When sp destroyed, wp becomes expired.
22 }
23 assert(wp.use_count() == 0 && wp.expired() == true);
24 func(wp);
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 721
Avoiding Circular References With std::weak_ptr
1 #include <memory>
2 #include <iostream>
3 #include <cassert>
4
5 struct Node {
6 Node(int id_) : id(id_) {}
7 ˜Node() {std::cout << "destroying node " << id << ’\n’;}
8 std::weak_ptr<Node> parent;
9 std::shared_ptr<Node> left;
10 std::shared_ptr<Node> right;
11 int id;
12 };
13
14 void func() {
15 std::shared_ptr<Node> root(std::make_shared<Node>(1));
16 assert(root.use_count() == 1);
17 root->left = std::make_shared<Node>(2);
18 assert(root.use_count() == 1 &&
19 root->left.use_count() == 1);
20 root->left->parent = root;
21 assert(root.use_count() == 1 &&
22 root->left.use_count() == 1);
23 // The reference count for each of the managed Node
24 // objects reaches zero, and these objects are
25 // destroyed.
26 // Node::˜Node is called twice here
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 722
Avoiding Circular References Example (Continued 1)
root
p
p p
cb
cb
l p
cb
r p
cb
i = 1
p
rc = 1
wc = 1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 723
Avoiding Circular References Example (Continued 2)
root
p
p p p p
cb
cb cb
l p l p
cb cb
r p r p
cb cb
i = 1 i = 2
p p
rc = 1 rc = 1
wc = 1 wc = 1
created new node, making it left child of root node (parent link not set)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 724
Avoiding Circular References Example (Continued 3)
root
p
p p p p
cb
cb cb
l p l p
cb cb
r p r p
cb cb
i = 1 i = 2
p p
rc = 1 rc = 1
wc = 2 wc = 1
set parent link (which is weak_ptr) for left child of root node
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 725
Avoiding Circular References Example (Continued 4)
root
p
p p p p
cb
cb cb
l p l p
cb cb
r p r p
cb cb
i = 1 i = 2
p p
rc = 0 rc = 1
wc = 2 wc = 1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 726
Avoiding Circular References Example (Continued 5)
root
p
p p p p
cb
cb cb
l p l p
cb cb
r p r p
cb cb
i = 1 i = 2
p p
rc = 0 rc = 1
wc = 2 wc = 1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 727
Avoiding Circular References Example (Continued 6)
root
p
p p p p
cb
cb cb
l p l p
cb cb
r p r p
cb cb
i = 1 i = 2
p p
rc = 0 rc = 0
wc = 2 wc = 1
started to destroy l (in root node); decremented use count, which reaches zero
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 728
Avoiding Circular References Example (Continued 7)
root
p
p p p p
cb
cb cb
l p l p
cb cb
r p r p
cb cb
i = 1 i = 2
p p
rc = 0 rc = 0
wc = 2 wc = 1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 729
Avoiding Circular References Example (Continued 8)
root
p
p p p p
cb
cb cb
l p l p
cb cb
r p r p
cb cb
i = 1 i = 2
p p
rc = 0 rc = 0
wc = 1 wc = 1
started to destroy p in left node; decremented weak count (which is not yet zero)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 730
Avoiding Circular References Example (Continued 9)
root
p
p p p p
cb
cb cb
l p l p
cb cb
r p r p
cb cb
i = 1 i = 2
p p
rc = 0 rc = 0
wc = 1 wc = 1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 731
Avoiding Circular References Example (Continued 10)
root
p
p p
cb
cb
l p
cb
r p
cb
i = 1
p p
rc = 0 rc = 0
wc = 1 wc = 1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 732
Avoiding Circular References Example (Continued 11)
root
p
p p
cb
cb
l p
cb
r p
cb
i = 1
p p
rc = 0 rc = 0
wc = 1 wc = 0
continue destruction of l in root node; decrement weak count, which reaches zero
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 733
Avoiding Circular References Example (Continued 12)
root
p
p p
cb
cb
l p
cb
r p
cb
i = 1
p
rc = 0
wc = 1
destroyed control block for (previously destroyed) left child of root node
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 734
Avoiding Circular References Example (Continued 13)
root
p
p p
cb
cb
l p
cb
r p
cb
i = 1
p
rc = 0
wc = 1
finished destroying l in root node; destroyed p in root node; and completed destruction of root node
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 735
Avoiding Circular References Example (Continued 14)
root
p
cb
p
rc = 0
wc = 1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 736
Avoiding Circular References Example (Continued 15)
root
p
cb
p
rc = 0
wc = 0
continuing with destruction of root; decremented weak count, which reaches zero
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 737
Avoiding Circular References Example (Continued 16)
root
p
cb
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 738
Avoiding Circular References Example (Continued 17)
root
p
cb
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 740
The boost::intrusive_ptr Class Template
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 741
The boost::intrusive_ptr Class Template (Continued 1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 742
intrusive_ptr Example
1 #include <boost/intrusive_ptr.hpp>
2 #include <iostream>
3 #include <string>
4 #include <cassert>
5
6 class Person {
7 public:
8 Person(const std::string& name) : name_(name),
9 refCount_(0) {}
10 void hold() {++refCount_;}
11 void release() {if (--refCount_ == 0) {delete this;}}
12 unsigned refCount() const {return refCount_;}
13 private:
14 ˜Person() {std::cout << "dtor called\n";}
15 std::string name_;
16 unsigned refCount_; // reference count
17 };
18
19 void intrusive_ptr_add_ref(Person* p) {p->hold();}
20 void intrusive_ptr_release(Person* p) {p->release();}
21
22 int main() {
23 boost::intrusive_ptr<Person> a(new Person("Bjarne"));
24 {
25 boost::intrusive_ptr<Person> b = a;
26 assert(a->refCount() == 2);
27 }
28 assert(a->refCount() == 1);
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 743
Section 3.2.6
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 744
Temporary Heap-Allocated Objects
1 #include <memory>
2
3 void func() {
4 // ...
5 int size = /* ... */;
6 auto buffer(std::make_unique<char[]>(size));
7 // ... (use buffer)
8 // when buffer destroyed, pointee automatically
9 // freed
10 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 745
Decoupled Has-A Relationship
instead of making object member of class, store object outside class and
make pointer to object member of class
might want to do this for object that:
is optional (e.g., is not always used or is lazily initialized)
has one of several base/derived types
pointer in class object owns decoupled object
1 #include <memory>
2
3 class Widget {
4 // ...
5 private:
6 // ...
7 std::unique_ptr<Type> item_;
8 // decoupled object has type Type
9 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 746
Decoupled Fixed-But-Dynamically-Sized Array
array stored outside class object, where array size fixed but determined at
run time
class object has pointer that owns decoupled array
1 #include <memory>
2
3 class Widget {
4 public:
5 using Element = int;
6 Widget(std::size_t size) :
7 array_(std::make_unique(Element[]>(size),
8 size_(size) {}
9 // ...
10 private:
11 // ...
12 const std::unique_ptr<Element[]> array_;
13 std::size_t size_;
14 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 747
Pimpl Idiom
interface and implementation split across two classes: (i.e., handle class
and body class)
known as pointer to implementation (pimpl) idiom
can be used, for example, to reduce compile-time dependencies (to
facilitate faster compiles)
class object has pointer that owns implementation object
1 #include <memory>
2
3 class Widget {
4 // ...
5 private:
6 class Impl; // defined elsewhere
7 const std::unique_ptr<Impl> impl_;
8 // incomplete type Impl is allowed
9 // ...
10 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 748
Tree
tree, where tree owns root node and each node owns its children
recursive destruction of nodes may cause stack-overflow problems,
especially for unbalanced trees (but such problems can be avoided by
dismantling tree from bottom upwards)
1 #include <memory>
2 #include <array>
3
4 class Tree {
5 public:
6 class Node {
7 // ...
8 private:
9 std::array<std::unique_ptr<Node>, 2> children_;
10 // owning pointers (parent owns children)
11 Node* parent_; // non-owning pointer
12 // ...
13 };
14 // ...
15 private:
16 std::unique_ptr<Node> root_;
17 // ...
18 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 749
Doubly-Linked List
doubly-linked list, where list owns first list node and each list node owns
its successor
recursive destruction of nodes can cause stack-overflow problems, for
sufficiently large lists (but deep recursions can be avoided with extra work)
1 #include <memory>
2
3 class List {
4 public:
5 class Node {
6 // ...
7 private:
8 std::unique_ptr<Node> next_;
9 // owning pointer (node owns successor)
10 Node* prev_; // non-owning pointer
11 };
12 // ...
13 private:
14 // ...
15 std::unique_ptr<Node> head_;
16 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 750
Tree That Provides Strong References
tree that provides strong references to data in nodes
tree owns root node and each node owns its children
accessor for node data returns object having pointer that keeps node alive
1 #include <memory>
2 #include <array>
3
4 class Tree {
5 public:
6 using Data = /* ... */;
7 class Node {
8 // ...
9 private:
10 std::array<std::shared_ptr<Node>, 2> children_;
11 std::weak_ptr<Node> parent_;
12 Data data_;
13 };
14 std::shared_ptr<Data> find(/* ... */) {
15 std::shared_ptr<Node> sp;
16 // ...
17 return {sp, &(sp->data)};
18 // use shared_ptr aliasing constructor
19 }
20 private:
21 std::shared_ptr<Node> root_;
22 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 751
Directed Acyclic Graph
encapsulated directed acyclic graph (DAG), where graph owns root nodes
and each node owns its children
pointers in graph object own root nodes
pointers in each node object owns children
1 #include <memory>
2 #include <vector>
3
4 class Dag {
5 public:
6 class Node {
7 // ...
8 private:
9 std::vector<std::shared_ptr<Node>> children_;
10 // owning pointers
11 std::vector<Node*> parents_;
12 // non-owning pointers
13 // ...
14 };
15 private:
16 std::vector<std::shared_ptr<Node>> roots_;
17 // owning pointers
18 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 752
Factory Function
1 #include <memory>
2
3 std::unique_ptr<Widget> makeWidget() {
4 return std::make_unique<Widget>();
5 }
6
7 std::shared_ptr<Gadget> makeGadget() {
8 return std::make_shared<Gadget>();
9 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 753
Factory Function With Cache
1 #include <memory>
2
3 std::shared_ptr<Widget> makeWidget(int id) {
4 static std::map<int, std::weak_ptr<Widget>> cache;
5 static std::mutex mut;
6 std::lock_guard<std::mutex> lock(mut);
7 auto sp = cache[id].lock();
8 if (!sp) {
9 sp = std::make_shared<Widget>(id);
10 cache[id] = sp;
11 }
12 return sp;
13 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 754
Section 3.2.7
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 755
Talks I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 756
Section 3.3
Exceptions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 757
Section 3.3.1
Introduction
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 758
Exceptions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 759
The Problem
High-Level
main
Code
Low-Level
Code
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 760
Traditional Error Handling
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 761
Example: Traditional Error Handling
1 #include <iostream>
2
3 bool func3() {
4 bool success = false;
5 // ...
6 return success;
7 }
8
9 bool func2() {
10 if (!func3()) {return false;}
11 // ...
12 return true;
13 }
14
15 bool func1() {
16 if (!func2()) {return false;}
17 // ...
18 return true;
19 }
20
21 int main() {
22 if (!func1()) {
23 std::cout << "failed\n";
24 return 1;
25 }
26 // ...
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 762
Error Handling With Exceptions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 763
Example: Exceptions
1 #include <iostream>
2 #include <stdexcept>
3
4 void func3() {
5 bool success = false;
6 // ...
7 if (!success) {throw std::runtime_error("Yikes!");}
8 }
9
10 void func2() {
11 func3();
12 // ...
13 }
14
15 void func1() {
16 func2();
17 // ...
18 }
19
20 int main() {
21 try {func1();}
22 catch (...) {
23 std::cout << "failed\n";
24 return 1;
25 }
26 // ...
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 764
safe_divide Example: Traditional Error Handling
1 #include <iostream>
2 #include <vector>
3 #include <utility>
4
5 std::pair<bool, int> safe_divide(int x, int y) {
6 if (!y) {
7 return std::make_pair(false, 0);
8 }
9 return std::make_pair(true, x / y);
10 }
11
12 int main() {
13 std::vector<std::pair<int, int>> v = {{10, 2}, {10, 0}};
14 for (auto p : v) {
15 auto result = safe_divide(p.first, p.second);
16 if (result.first) {
17 int quotient = result.second;
18 std::cout << quotient << ’\n’;
19 } else {
20 std::cerr << "division by zero\n";
21 }
22 }
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 765
safe_divide Example: Exceptions
1 #include <iostream>
2 #include <vector>
3 #include <utility>
4
5 class divide_by_zero {};
6
7 int safe_divide(int x, int y) {
8 if (!y) {
9 throw divide_by_zero();
10 }
11 return x / y;
12 }
13
14 int main() {
15 std::vector<std::pair<int, int>> v = {{10, 2}, {10, 0}};
16 for (auto p : v) {
17 try {
18 std::cout << safe_divide(p.first, p.second) <<
19 ’\n’;
20 }
21 catch(const divide_by_zero& e) {
22 std::cerr << "division by zero\n";
23 }
24 }
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 766
Exceptions Versus Traditional Error Handling
advantages of exceptions:
exceptions allow for error handling code to be easily separated from code
that detects error
exceptions can easily pass error information many levels up call chain
passing of error information up call chain managed by language (no explicit
code required)
disadvantages of exceptions:
writing code that always behaves correctly in presence of exceptions
requires great care (as we shall see)
although possible to have no execution-time cost when exceptions not
thrown, still have memory cost (to store information needed for stack
unwinding for case when exception is thrown)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 767
Section 3.3.2
Exceptions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 768
Exceptions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 769
Standard Exception Classes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 770
Standard Exception Classes (Continued 1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 771
Standard Exception Classes (Continued 2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 772
Section 3.3.3
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 773
Throwing Exceptions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 774
Throwing Exceptions (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 775
Catching Exceptions
exception can be caught by catch clause of try-catch block
code that might throw exception placed in try block
code to handle exception placed in catch block
try-catch block can have multiple catch clauses
catch clauses checked for match in order specified and only first match
used
catch (...) can be used to catch any exception
example:
try {
// code that might throw exception
}
catch (const std::logic_error& e) {
// handle logic_error exception
}
catch (const std::runtime_error& e) {
// handle runtime_error exception
}
catch (...) {
// handle other exception types
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 776
Catching Exceptions (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 777
Exception During Exception: Catching By Value
1 #include <iostream>
2 #include <stdexcept>
3
4 class Error {
5 public:
6 Error(int value) : value_(value) {}
7 Error(Error&& e) : value_(e.value_) {}
8 Error(const Error&) {throw std::runtime_error("copy");}
9 int get() const {return value_;}
10 private:
11 int value_; // error code
12 };
13
14 void func2() {throw Error(42);} // might move
15
16 void func1() {
17 try {func2();}
18 // catch by value (copy throws)
19 catch (Error e) {
20 std::cerr << "yikes\n";
21 }
22 }
23
24 int main() {
25 try {func1();}
26 catch (...) {std::cerr << "exception\n";}
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 778
Throwing Polymorphically: Failed Attempt
1 #include <iostream>
2
3 class Base {};
4 class Derived : public Base {};
5
6 void func(Base& x) {
7 throw x; // always throws Base
8 }
9
10 int main() {
11 Derived d;
12 try {func(d);}
13 catch (Derived& e) {
14 std::cout << "Derived\n";
15 }
16 catch (...) {
17 std::cout << "not Derived\n";
18 }
19 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 779
Throwing Polymorphically
1 #include <iostream>
2
3 class Base {
4 public:
5 virtual void raise() {throw *this;}
6 };
7 class Derived : public Base {
8 public:
9 virtual void raise() {throw *this;}
10 };
11
12 void func(Base& x) {
13 x.raise();
14 }
15
16 int main() {
17 Derived d;
18 try {func(d);}
19 catch (Derived& e) {
20 std::cout << "Derived\n";
21 }
22 catch (...) {
23 std::cout << "not Derived\n";
24 }
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 780
Rethrowing Exceptions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 781
Rethrowing Example: Exception Dispatcher Idiom
1 void handle_exception() {
2 try {throw;}
3 catch (const exception_1& e) {
4 log_error("exception_1 occurred");
5 // ...
6 }
7 catch (const exception_2& e) {
8 log_error("exception_2 occurred");
9 // ...
10 }
11 // ...
12 }
13
14 void func() {
15 try {operation();}
16 catch (...) {handle_exception();}
17 // ...
18 try {another_operation();}
19 catch (...) {handle_exception();}
20 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 782
Transfer of Control from Throw Site to Handler
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 783
Stack Unwinding Example
1 void func1() {
2 std::string dave("dave");
3 try {
4 std::string bye("bye");
5 func2();
6 }
7 catch (const std::runtime_error& e) { // Handler
8 std::cerr << e.what() << ’\n’;
9 }
10 }
11
12 void func2() {
13 std::string world("world");
14 func3(0);
15 }
16
17 void func3(int x) {
18 std::string hello("hello");
19 if (x == 0) {
20 std::string first("first");
21 std::string second("second");
22 throw std::runtime_error("yikes"); // Throw site
23 }
24 }
calling func1 will result in exception being thrown in func3
during stack unwinding, destructors called in order for second, first, hello,
world, and bye (i.e., reverse order of construction); dave unaffected
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 784
Function Try Blocks
function try blocks allow entire function to be wrapped in try block
function returns when control flow reaches end of catch block (return
statement needed for non-void function)
example:
1 #include <iostream>
2 #include <stdexcept>
3
4 int main()
5 try {
6 throw std::runtime_error("yikes");
7 }
8 catch (const std::runtime_error& e) {
9 std::cerr << "runtime error " << e.what() << ’\n’;
10 }
although function try blocks can be used for any function, most important
use cases are for constructors and destructors
function try block only way to catch exceptions thrown during construction
of data members or base objects (which happens before constructor body
is entered) or during destruction of data members or base objects (which
happens after destructor body exited)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 785
Exceptions and Construction/Destruction
order of construction:
1 base class objects as listed in type definition left to right
2 data members as listed in type definition top to bottom
3 constructor body
order of destruction is exact reverse of order of construction, namely:
1 destructor body
2 data members as listed in type definition bottom to top
3 base class objects as listed in type definition right to left
lifetime of object begins when constructor completes
constructor might throw in:
constructor of base class object
constructor of data member
constructor body
need to perform cleanup for constructor body
will assume destructors do not throw (since very bad idea to throw in
destructor)
any exception caught in function try block of constructor or destructor
rethrown implicitly (at end of catch block)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 786
Construction/Destruction Example
1 #include <string>
2 #include <iostream>
3
4 struct Base {
5 Base() {}
6 ˜Base() {};
7 };
8
9 class Widget : public Base {
10 public:
11 Widget() {}
12 ˜Widget() {}
13 // ...
14 private:
15 std::string s_;
16 std::string t_;
17 };
18
19 int main() {
20 Widget w;
21 // ...
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 787
Function Try Block Example
1 #include <iostream>
2 #include <stdexcept>
3
4 class Gadget {
5 public:
6 Gadget() {throw std::runtime_error("ctor");}
7 ˜Gadget() {}
8 };
9
10 class Widget {
11 public:
12 // constructor uses function try block
13 Widget()
14 try {std::cerr << "ctor body\n";}
15 catch (...) {std::cerr << "exception in ctor\n";}
16 ˜Widget() {std::cerr << "dtor body\n";}
17 private:
18 Gadget g_;
19 };
20
21 int main()
22 try {Widget w;}
23 catch (...) {
24 std::cerr << "terminating due to exception\n";
25 return 1;
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 788
Section 3.3.4
Exception Specifications
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 789
The noexcept Specifier
noexcept specifier in function declaration indicates whether or not
function can throw exceptions
noexcept specifier with bool constant expression argument indicates
function does not throw exceptions if expression true (otherwise, may
throw)
noexcept without argument equivalent to noexcept(true)
except for destructors, not providing noexcept specifier equivalent to
noexcept(false)
if noexcept specifier not provided for destructor, specifier identical to
that of implicit declaration (which is, in practice, usually noexcept)
example:
void func1(); // may throw anything
void func2() noexcept(false); // may throw anything
void func3() noexcept(true); // does not throw
void func4() noexcept; // does not throw
template <class T>
void func5(T) noexcept(sizeof(T) <= 4);
// does not throw if sizeof(T) <= 4
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 790
The noexcept Specifier (Continued 1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 791
The noexcept Specifier (Continued 2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 792
The noexcept Specifier (Continued 3)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 793
Exceptions and Function Calls
for some (nonreference) class type T and some constant bool
expression expr, consider code such as:
T func(T) noexcept(expr);
T x;
T y = func(x); // function call
function call can throw exception as result of:
1 parameter passing (if pass by value)
2 function execution including return statement
in parameter passing, construction and destruction of each parameter
happens in context of calling function
consequently, invocation of noexcept function can still result in exception
being thrown due to parameter passing
in case of return by value, construction of temporary (if not elided) to hold
return value happens in context of called function
consequently, must exercise care not to violate noexcept contract if
noexcept function returns by value
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 794
Avoiding Exceptions Due to Function Calls
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 795
noexcept Operator
noexcept operator takes expression and returns bool indicating if
expression can throw exception
does not actually evaluate expression
in determining result, only considers noexcept specifications for
functions involved
example:
1 #include <cstdlib>
2 #include <cassert>
3 #include <utility>
4
5 void increment(int&) noexcept;
6 char* memAlloc(std::size_t);
7
8 // does not throw exception, but not declared noexcept
9 void doesNotThrow() {};
10
11 int main() {
12 assert(noexcept(1 + 1) == true);
13 assert(noexcept(memAlloc(0)) == false);
14 // Note: does not evaluate expression
15 assert(noexcept(increment(*((int*)0))) == true);
16 assert(noexcept(increment(std::declval<int&>())) ==
17 true);
18 // Note: only uses noexcept specifiers
19 assert(noexcept(doesNotThrow()) == false);
20 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 796
noexcept Operator (Continued)
noexcept operator particularly useful for templates
example:
1 #include <iostream>
2
3 class Int256 { /* ... */ }; // 256-bit integer
4 class BigInt { /* ... */ }; // arbitrary-precision integer
5
6 // function will not throw exception
7 Int256 operator+(const Int256& x, const Int256& y)
8 noexcept;
9
10 // function may throw exception
11 BigInt operator+(const BigInt& x, const BigInt& y);
12
13 // whether function may throw exception depends on T
14 template <class T>
15 T add(const T& x, const T& y) noexcept(noexcept(x + y) &&
16 std::is_nothrow_move_constructible<T>::value)
17 {return x + y;}
18
19 int main() {
20 Int256 i1, i2;
21 BigInt b1, b2;
22 std::cout << "int " << noexcept(add(1, 1)) << ’\n’
23 << "Int256 " << noexcept(add(i1, i2)) << ’\n’
24 << "BigInt " << noexcept(add(b1, b2)) << ’\n’;
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 797
Dynamic Exception Specifications
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 798
Section 3.3.5
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 799
Storing and Retrieving Exceptions
might want to store exception and then later retrieve and rethrow it
exception can be stored using std::exception_ptr type
current exception can be retrieved with std::current_exception
rethrow exception stored in exception_ptr object using
std::rethrow_exception
provides mechanism for moving exceptions between threads:
store exception on one thread
then retrieve and rethrow stored exception on another thread
std::make_exception_ptr can be used to make exception_ptr
object
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 800
Example: Storing and Retrieving Exceptions
1 #include <exception>
2 #include <stdexcept>
3
4 void yikes() {
5 throw std::runtime_error("Yikes!");
6 }
7
8 std::exception_ptr getException() {
9 try {
10 yikes();
11 }
12 catch (...) {
13 return std::current_exception();
14 }
15 return nullptr;
16 }
17
18 int main() {
19 std::exception_ptr e = getException();
20 std::rethrow_exception(e);
21 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 801
Section 3.3.6
Exception Safety
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 802
Resource Management
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 803
Resource Leak Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 804
Cleanup
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 805
Exception Safety and Exception Guarantees
being satisfied (e.g., for std::vector, element type has nonthrowing move
or is copyable)
insert on std::list
examples of nothrow guarantee:
swap of two containers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 807
Resource Acquisition Is Initialization (RAII)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 808
Resource Leak Example Revisited
implementation 1 (not exception safe; has memory leak):
1 void useBuffer(char* buf) { /* ... */ }
2
3 void doWork() {
4 char* buf = new char[1024];
5 useBuffer(buf);
6 delete[] buf;
7 }
implementation 2 (exception safe):
1 template <class T>
2 class SmartPtr {
3 public:
4 SmartPtr(int size) : ptr_(new T[size]) {}
5 ˜SmartPtr() {delete[] ptr_;}
6 operator T*() {return ptr_;}
7 // ...
8 private:
9 T* ptr_;
10 };
11
12 void useBuffer(char* buf) { /* ... */ }
13
14 void doWork() {
15 SmartPtr<char> buf(1024);
16 useBuffer(buf);
17 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 809
Section 3.3.7
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 810
Implementation of Exception Handling
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 811
Implementation of Exception Handling (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 812
Appropriateness of Using Exceptions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 814
Section 3.3.8
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 815
TwoBufs Example With Resource Leak
1 #include <cstddef>
2 #include <limits>
3
4 class TwoBufs {
5 public:
6 TwoBufs(std::size_t aSize, std::size_t bSize) :
7 a_(nullptr), b_(nullptr) {
8 a_ = new char[aSize];
9 // If new throws, a_ will be leaked.
10 b_ = new char[bSize];
11 }
12 ˜TwoBufs() {
13 delete[] a_;
14 delete[] b_;
15 }
16 // ...
17 private:
18 char* a_;
19 char* b_;
20 };
21
22 void doWork() {
23 // This may leak memory.
24 TwoBufs x(1000000,
25 std::numeric_limits<std::size_t>::max());
26 // ...
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 816
TwoBufs Example Corrected With unique_ptr
1 #include <cstddef>
2 #include <limits>
3 #include <memory>
4
5 class TwoBufs {
6 public:
7 TwoBufs(std::size_t aSize, std::size_t bSize) :
8 a_(std::make_unique<char[]>(aSize)),
9 b_(std::make_unique<char[]>(bSize)) {}
10 ˜TwoBufs() {}
11 // ...
12 private:
13 std::unique_ptr<char[]> a_;
14 std::unique_ptr<char[]> b_;
15 };
16
17 void doWork() {
18 // This will not leak memory.
19 TwoBufs x(1000000,
20 std::numeric_limits<std::size_t>::max());
21 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 817
RAII Example: Stream Formatting Flags
1 #include <iostream>
2 #include <ios>
3 #include <boost/io/ios_state.hpp>
4
5 // not exception safe
6 void unsafeOutput(std::ostream& out, unsigned int x) {
7 auto flags = out.flags();
8 // if exception thrown during output of x, old
9 // formatting flags will not be restored
10 out << std::hex << std::showbase << x << ’\n’;
11 out.flags(flags);
12 }
13
14 // exception safe
15 void safeOutput(std::ostream& out, unsigned int x) {
16 boost::io::ios_flags_saver ifs(out);
17 out << std::hex << std::showbase << x << ’\n’;
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 818
Other RAII Examples
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 819
Section 3.3.9
Exception Gotchas
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 820
shared_ptr Example: Not Exception Safe (Prior to C++17)
1 #include <memory>
2
3 class T1 { /* ... */ };
4 class T2 { /* ... */ };
5
6 void func(std::shared_ptr<T1> p, std::shared_ptr<T2> q)
7 { /* ... */ }
8
9 void doWork() {
10 // potential memory leak
11 func(std::shared_ptr<T1>(new T1),
12 std::shared_ptr<T2>(new T2));
13 // ...
14 }
one problematic order: another problematic order:
2 call func
each of T1 and T2 objects managed by shared_ptr at all times so no
memory leak possible if exception thrown
similar issue arises in context of std::unique_ptr and can be resolved
by using std::make_unique in similar way as above
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 822
Stack Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 823
Section 3.3.10
Miscellany
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 824
safe_add Example: Traditional Error Handling
1 #include <limits>
2 #include <vector>
3 #include <iostream>
4
5 std::pair<bool, int> safe_add(int x, int y) {
6 return ((y > 0 && x > std::numeric_limits<int>::max() - y)
7 || (y < 0 && x < std::numeric_limits<int>::min() - y)) ?
8 std::make_pair(false, 0) : std::make_pair(true, x + y);
9 }
10
11 int main() {
12 constexpr int int_min = std::numeric_limits<int>::min();
13 constexpr int int_max = std::numeric_limits<int>::max();
14 std::vector<std::pair<int, int>> v{
15 {int_max, int_max}, {1, 2}, {int_min, int_min},
16 {int_max, int_min}, {int_min, int_max}
17 };
18 for (auto x : v) {
19 auto result = safe_add(x.first, x.second);
20 if (result.first) {
21 std::cout << result.second << ’\n’;
22 } else {
23 std::cout << "overflow\n";
24 }
25 }
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 825
safe_add Example: Exceptions
1 #include <limits>
2 #include <vector>
3 #include <iostream>
4 #include <stdexcept>
5
6 int safe_add(int x, int y) {
7 return ((y > 0 && x > std::numeric_limits<int>::max() - y)
8 || (y < 0 && x < std::numeric_limits<int>::min() - y)) ?
9 throw std::overflow_error("addition") : x + y;
10 }
11
12 int main() {
13 constexpr int int_min = std::numeric_limits<int>::min();
14 constexpr int int_max = std::numeric_limits<int>::max();
15 std::vector<std::pair<int, int>> v{
16 {int_max, int_max}, {1, 2}, {int_min, int_min},
17 {int_max, int_min}, {int_min, int_max}
18 };
19 for (auto x : v) {
20 try {
21 int result = safe_add(x.first, x.second);
22 std::cout << result << ’\n’;
23 }
24 catch (const std::overflow_error&) {
25 std::cout << "overflow\n";
26 }
27 }
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 826
Section 3.3.11
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 827
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 829
Talks I
1 Jon Kalb. Exception-Safe Code, CppCon, Bellevue, WA, USA, Sep 7–12,
2014. Available online at https://youtu.be/W7fIy_54y-w, https://
youtu.be/b9xMIKb1jMk, and https://youtu.be/MiKxfdkMJW8. (This
talk is in three parts.)
2 Jon Kalb. Exception-Safe Coding, C++Now, Aspen, CO, USA, May 13–18,
2012. Available online at https://youtu.be/N9bR0ztmmEQ and
https://youtu.be/UiZfODgB-Oc. (This talk is in two parts.)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 830
Section 3.4
Rvalue References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 831
Section 3.4.1
Introduction
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 832
Motivation Behind Rvalue References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 833
Terminology: Named and Cv-Qualified
A type that includes one or both of the qualifiers const and volatile
is called a cv-qualified type.
A type that is not cv-qualified is called cv-unqualified.
Example:
The types const int and volatile char are cv-qualified.
The types int and char are cv-unqualified.
An object or function that is named by an identifier is said to be named.
An object or function that cannot be referred to by name is said to be
unnamed.
Example:
std::vector<int> v = {1, 2, 3, 4};
std::vector<int> w;
w = v; // w and v are named
w = std::vector<int>(2, 0);
// w is named
// std::vector<int>(2, 0) is unnamed
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 834
Section 3.4.2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 835
Propagating Values: Copying and Moving
Suppose that we have two objects of the same type and we want to
propagate the value of one object (i.e., the source) to the other object (i.e.,
the destination).
This can be accomplished in one of two ways:
1 copying; or
2 moving.
Copying propagates the value of the source object to the destination
object without modifying the source object.
Moving propagates the value of the source object to the destination
object and is permitted to modify the source object.
Moving is always at least as efficient as copying, and for many types,
moving is more efficient than copying.
For some types, copying does not make sense, while moving does (e.g.,
std::ostream, std::istream).
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 836
Vector Example: Moving Versus Copying
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 837
Vector Example: Copying
code for copying from source src to destination dst (not self assignment):
delete [] dst.data_;
dst.data_ = new T[src.size_];
dst.size_ = src.size_;
std::copy_n(src.data_, src.size_, dst.data_);
copying requires: one array delete (destruction, memory deallocation),
one array new (memory allocation, construction), copying of element data
(copy assignment, etc.), and updating data_ and size_ data members
copying proceeds as follows:
src dst
data_ s0 data_ d0
size_ n s1 size_ m d1
.. ..
. .
sn−1 dm−1
src dst
data_ s0 data_ s0
size_ n s1 size_ n s1
.. ..
. .
sn−1 sn−1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 838
Vector Example: Moving
code for moving from source src to destination dst:
std::swap(src.data_, dst.data_);
std::swap(src.size_, dst.size_);
moving only requires updating data_ and size_ data members
although not considered here, could also free data array associated with
src if desirable to release memory as soon as possible
moving proceeds as follows:
src dst
data_ s0 data_ d0
size_ n s1 size_ m d1
.. ..
. .
sn−1 dm−1
src dst
data_ s0 data_ d0
size_ m s1 size_ n d1
.. ..
. .
sn−1 dm−1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 839
Moving Versus Copying
Moving is usually more efficient than copying, often by very large margin.
So, we should prefer moving to copying.
We can safely replace a copy by a move when subsequent code does not
depend on the value of source object.
It would be convenient if the language could provide a mechanism for
automatically using a move (instead of a copy) in situations where doing
so is always guaranteed to be safe.
For reasons of efficiency, it would also be desirable for the language to
provide a mechanism whereby the programmer can override the normal
behavior and force a move (instead of a copy) in situations where such a
transformation is known to be safe only due to some special additional
knowledge about program behavior.
Rvalue references provide the above mechanisms.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 840
Section 3.4.3
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 841
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 842
Expressions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 843
Categories of Expressions
expression
glvalue
rvalue
Every expression can be classified into exactly one of the three following
categories:
1 lvalue
2 prvalue (pure rvalue)
3 xvalue (expiring value)
An expression that is an lvalue or xvalue is called a glvalue (generalized
lvalue).
An expression that is a prvalue or an xvalue is called an rvalue.
Every expression is either an lvalue or an rvalue (but not both).
Whether or not it is safe to move (instead of copy) depends on whether an
lvalue or rvalue is involved.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 844
Lvalues
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 845
Lvalues (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 846
Moving and Lvalues
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 847
Prvalues
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 848
Prvalues (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 849
Moving and Prvalues
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 850
Xvalues
An xvalue (expiring value) is an expression that:
refers to an object (usually near the end of its lifetime);
has an identity; and
is deemed to be safe to use as the source for a move.
An xvalue is a kind of rvalue.
An xvalue is the result of certain kinds of expressions involving rvalue
references.
The result of calling a function whose return type is an rvalue reference
type is an xvalue. Example:
std::string s("Hello");
std::string t = std::move(s); // std::move(s) is xvalue
In the above example, the template function std::move converts its
argument to an xvalue (since it returns an rvalue reference type).
Unnamed rvalue references to objects are xvalues.
std::string s("Hello");
std::string t;
t = static_cast<std::string&&>(s);
// static_cast<std::string&&>(s) is xvalue
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 851
Moving and Xvalues
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 852
Moving and Lvalues and Rvalues
With regard to propagating the value from one object to another, we can
summarize the preceding results as given below.
If the source is an rvalue (i.e., prvalue or xvalue), using a move instead of
a copy is always safe.
If the source is an lvalue, using a move instead of a copy is not
guaranteed to be safe.
It would be highly desirable if the language would provide a mechanism
that would automatically allow a move to be used in the rvalue case and a
copy to be employed otherwise.
In fact, this is exactly what the language does.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 853
More on Lvalues and Rvalues
Class rvalues can have cv-qualified types, while non-class rvalues always
have cv-unqualified types. Example:
const int getConstInt(); // const is ignored
const std::string getConstString();
int i = getConstInt();
// getConstInt() is modifiable rvalue of type int
// (not const int)
std::string s = getConstString();
// getConstString() is nonmodifiable rvalue
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 854
Exercise: Expressions
1 #include <iostream>
2 #include <string>
3 #include <utility>
4
5 std::string&& func1(std::string& x) {
6 return std::move(x);
7 // x? std::move(x)?
8 }
9
10 int main() {
11 const std::string hello("Hello");
12 std::string a;
13 std::string b;
14
15 a = hello + "!";
16 // hello? hello + "!"? a = hello + "!"?
17 std::cout << a << ’\n’;
18 // std::cout? std::cout << a?
19
20 a = std::string("");
21 // std::string("")? a = std::string("")?
22 ((a += hello) += "!");
23 // a += hello?
24 b = func1(a);
25 // func1(a)? b = func1(a)?
26 std::cout << b << ’\n’;
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 855
Built-In Operators, Rvalues, and Lvalues
Aside from the exceptions noted below, all of the built-in operators require
operands that are rvalues.
The operand of each of the following built-in operators must be an lvalue:
address of (i.e., unary &),
Aside from the exceptions noted below, all of the built-in operators yield a
result that is an rvalue.
The following operators yield a result that is an lvalue:
subscript (i.e., [])
dereference (i.e., unary *)
Whether an operator for a class type requires operands that are lvalues or
rvalues or yield lvalues or rvalues is determined by the parameter types
and return type of the operator function.
The member selection operator may yield an lvalue or rvalue, depending
on the particular manner in which the operator is used. (The behavior is
fairly intuitive.)
The lvalue/rvalue-ness and type of the result produced by the ternary
conditional operator depends on the particular manner in which the
operator is employed.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 857
Implicit Lvalue-to-Rvalue Conversion
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 858
Section 3.4.4
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 859
References: Binding and Overload Resolution
The kinds of expressions, to which lvalue and rvalue references can bind,
differ.
For a nonreference type T (such as int or const int), what kinds of
expressions can validly be placed in each of the boxes in the example
below?
T& r = ;
T&& r = ;
Lvalue and rvalue references also behave differently with respect to
overload resolution.
Let T be a cv-unqualified nonreference type. Which overloads of func will
be called in the example below?
T operator+(const T&, const T&);
void func(const T&);
void func(T&&);
T x;
func(x); // calls which version of func?
func(x + x); // calls which version of func?
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 860
Reference Binding
Again, the loss of cv qualifiers must be avoided for const and volatile
correctness.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 861
Reference Binding (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 862
Why Rvalue References Cannot Bind to Lvalues
In effect, rvalue references were introduced into the language to allow a
function to know if one of its reference parameters is bound to an object
whose value is safe to change without impacting other code, namely, an
rvalue (i.e., a temporary object or xvalue).
Since an rvalue reference can only bind to an rvalue, any rvalue reference
parameter to a function is guaranteed to be bound to a temporary object
or xvalue.
Example:
class Thing {
public:
// Move constructor
// parameter x known to be safe to use as source for move
Thing(Thing&& x);
// Move assignment operator
// parameter x known to be safe to use as source for move
Thing& operator=(Thing&& x);
// ...
};
// parameter x known to be safe to modify
void func(Thing&& x);
If rvalue references could bind to lvalues, the above guarantee could not
be made, as an rvalue reference could then refer to an object whose value
cannot be changed safely, namely, an lvalue.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 863
Why Non-Const Lvalue References Cannot Bind to Rvalues
void func(int& x) {
// ...
}
int main() {
int i = 1;
int j = 2;
func(i + j);
// ERROR: cannot bind non-const lvalue
// reference to rvalue
// What would be consequence if allowed?
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 864
Reference Binding Summary
Rvalue Lvalue
const const
const volatile const volatile
T volatile T volatile
T T T T
T T
T&& ✓ C V C,V ✗ ✗ ✗ ✗
const
✓ ✓ V V ✗ ✗ ✗ ✗
T&&
volatile
✓ ✗ ✓ C ✗ ✗ ✗ ✗
T&&
const
volatile ✓ ✓ ✓ ✓ ✗ ✗ ✗ ✗
T&&
T& ✗ ✗ ✗ ✗ ✓ C V C,V
const T& ✓ ✓ V V ✓ ✓ V V
volatile
✗ ✗ ✗ ✗ ✓ C ✓ C
T&
const
volatile ✗ ✗ ✗ ✗ ✓ ✓ ✓ ✓
T&
✓: allowed C: strips const V: strips volatile ✗: other
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 865
Reference Binding Example
1 #include <string>
2 using std::string;
3
4 string value() {
5 return string("Hello");
6 }
7
8 const string constValue() {
9 return string("World");
10 }
11
12 int main() {
13 string i("mutable");
14 const string j("const");
15
16 string& r01 = i;
17 string& r02 = j; // ERROR: drops const
18 string& r03 = value(); // ERROR: non-const lvalue reference from rvalue
19 string& r04 = constValue(); // ERROR: non-const lvalue reference from rvalue
20
21 const string& r05 = i;
22 const string& r06 = j;
23 const string& r07 = value();
24 const string& r08 = constValue();
25
26 string&& r09 = i; // ERROR: rvalue reference from lvalue
27 string&& r10 = j; // ERROR: rvalue reference from lvalue
28 string&& r11 = value();
29 string&& r12 = constValue(); // ERROR: drops const
30
31 const string&& r13 = i; // ERROR: rvalue reference from lvalue
32 const string&& r14 = j; // ERROR: rvalue reference from lvalue
33 const string&& r15 = value();
34 const string&& r16 = constValue();
35 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 866
Overload Resolution
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 867
Overload Resolution Summary
Priority
Rvalue Lvalue
const const
const volatile const volatile
T volatile T volatile
T T T T
T T
T&& 1
const
2 1
T&&
volatile
2 1
T&&
const
volatile 3 2 2 1
T&&
T& 1
const T& 4 3 2 1
volatile
2 1
T&
const
volatile 3 2 2 1
T&
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 868
Overloading Example 1
1 #include <iostream>
2 #include <string>
3
4 void func(std::string& x) {
5 std::cout << "func(std::string&) called\n";
6 }
7
8 void func(const std::string& x) {
9 std::cout << "func(const std::string&) called\n";
10 }
11
12 void func(std::string&& x) {
13 std::cout << "func(std::string&&) called\n";
14 }
15
16 void func(const std::string&& x) {
17 std::cout << "func(const std::string&&) called\n";
18 }
19
20 const std::string&& constValue(const std::string&& x) {
21 return static_cast<const std::string&&>(x);
22 }
23
24 int main() {
25 const std::string cs("hello");
26 std::string s("world");
27 func(s);
28 func(cs);
29 func(cs + s);
30 func(constValue(cs + s));
31 }
32
33 /* Output:
34 func(std::string&) called
35 func(const std::string&) called
36 func(std::string&&) called
37 func(const std::string&&) called
38 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 869
Overloading Example 2
1 #include <iostream>
2 #include <string>
3
4 void func(const std::string& x) {
5 std::cout << "func(const std::string&) called\n";
6 }
7
8 void func(std::string&& x) {
9 std::cout << "func(std::string&&) called\n";
10 }
11
12 const std::string&& constValue(const std::string&& x) {
13 return static_cast<const std::string&&>(x);
14 }
15
16 int main() {
17 const std::string cs("hello");
18 std::string s("world");
19 func(s);
20 func(cs);
21 func(cs + s);
22 func(constValue(cs + s));
23 }
24
25 /* Output:
26 func(const std::string&) called
27 func(const std::string&) called
28 func(std::string&&) called
29 func(const std::string&) called
30 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 870
Why Rvalue References Cannot Bind to Lvalues (Revisited)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 871
Section 3.4.5
Moving
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 872
Move Constructors
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 873
Move Assignment Operators
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 874
Vector Example Revisited
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 875
Example Without Move Construction/Assignment
1 #include <algorithm>
2 #include <complex>
3
4 template <class T>
5 class Vector {
6 public:
7 Vector(unsigned int size, T value = 0) : data_(new T[size]), size_(size)
8 {std::fill_n(data_, size, value);}
9 Vector(const Vector& a) : data_(new T[a.size_]), size_(a.size_)
10 {std::copy_n(a.data_, a.size_, data_);}
11 Vector& operator=(const Vector& a) {
12 if (this != &a) {
13 delete[] data_; size_ = a.size_; data_ = new T[a.size_];
14 std::copy_n(a.data_, a.size_, data_);
15 }
16 return *this;
17 }
18 ˜Vector() {delete[] data_;}
19 // ...
20 private:
21 T* data_; // pointer to element data
22 unsigned int size_; // number of elements
23 };
24 using Vec = Vector<std::complex<double>>;
25 Vec getVector() {return Vec(1000, {0.0, 1.0});}
26
27 int main() {
28 Vec v(0);
29 Vec w = getVector(); // construct from temporary object
30 v = Vec(2000, {1.0, 2.0}); // assign from temporary object
31 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 876
Example With Move Construction/Assignment
1 #include <algorithm>
2 #include <complex>
3
4 template <class T>
5 class Vector {
6 public:
7 Vector(unsigned int size, T value = 0) : data_(new T[size]), size_(size)
8 {std::fill_n(data_, size, value);}
9 Vector(const Vector& a) : data_(new T[a.size_]), size_(a.size_)
10 {std::copy_n(a.data_, a.size_, data_);}
11 Vector& operator=(const Vector& a) {
12 if (this != &a) {
13 delete[] data_; size_ = a.size_; data_ = new T[a.size_];
14 std::copy_n(a.data_, a.size_, data_);
15 }
16 return *this;
17 }
18 // Move constructor
19 Vector(Vector&& a) : data_(a.data_), size_(a.size_)
20 {a.size_ = 0; a.data_ = nullptr;}
21 // Move assignment operator
22 Vector& operator=(Vector&& a) {
23 std::swap(size_, a.size_); std::swap(data_, a.data_);
24 return *this;
25 }
26 ˜Vector() {delete[] data_;}
27 // ...
28 private:
29 T* data_; // pointer to element data
30 unsigned int size_; // number of elements
31 };
32 using Vec = Vector<std::complex<double>>;
33 Vec getVector() {return Vec(1000, {0.0, 1.0});}
34
35 int main() {
36 Vec v(0);
37 Vec w = getVector(); // construct from temporary object
38 v = Vec(2000, {1.0, 2.0}); // assign from temporary object
39 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 877
Allowing Move Semantics in Other Contexts via std::move
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 878
Old-Style Swap
In the above code, a swap requires three copy operations (namely, one
copy constructor call and two copy assignment operator calls).
For many types T, this use of copying is very inefficient.
Furthermore, the above code requires that T must be copyable (i.e., T has
a copy constructor and copy assignment operator).
In C++11, we can write a much better swap function.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 879
Improved Swap
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 880
Moving Versus Copying Example
1 #include <iostream>
2 #include <utility>
3
4 class Widget {
5 public:
6 Widget() {}
7 ˜Widget() {}
8 Widget(Widget&&) {std::cout << "move construct\n";}
9 Widget(const Widget&) {std::cout << "copy construct\n";}
10 Widget& operator=(Widget&&)
11 {std::cout << "move assign\n"; return *this;}
12 Widget& operator=(const Widget&)
13 {std::cout << "copy assign\n"; return *this;}
14 // ...
15 };
16
17 Widget make_widget_1() {
18 return Widget(); // NOTE: Returns temporary.
19 }
20
21 Widget make_widget_2() {
22 Widget w;
23 return w; // NOTE: Returns named object.
24 }
25
26 int main() {
27 Widget a;
28 Widget b(a); // copy construct
29 Widget c(std::move(b)); // move construct
30 Widget d(make_widget_1()); // guaranteed copy/move elision
31 Widget e(make_widget_2()); // move construct if no NRVO
32 c = a; // copy assign
33 b = std::move(c); // move assign
34 a = make_widget_1(); // move assign
35 b = make_widget_2(); // move construct if no NRVO; move assign
36 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 881
Implication of Rvalue-Reference Type Function Parameters
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 883
Reference-Qualified Member Functions Example
1 #include <iostream>
2
3 class Widget {
4 public:
5 void func() const &
6 {std::cout << "const lvalue\n";}
7 void func() &
8 {std::cout << "non-const lvalue\n";}
9 void func() const &&
10 {std::cout << "const rvalue\n";}
11 void func() &&
12 {std::cout << "non-const rvalue\n";}
13 };
14
15 const Widget getConstWidget() {return Widget();}
16
17 int main(){
18 Widget w;
19 const Widget cw;
20 w.func(); // non-const lvalue
21 cw.func(); // const lvalue
22 Widget().func(); // non-const rvalue
23 getConstWidget().func(); // const rvalue
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 884
Lvalueness/Rvalueness and the *this Parameter
1 class Int {
2 public:
3 Int(int x = 0) : value_(x) {}
4 // only allow prefix increment for lvalues
5 Int& operator++() & {++value_; return *this;}
6 // The following allows prefix increment for rvalues:
7 // Int& operator++() {++value_; return *this;}
8 // ...
9 private:
10 int value_;
11 };
12
13 int one() {return 1;}
14
15 int main() {
16 int i = 0;
17 int j = ++i; // OK
18 // int k = ++one(); // ERROR (not lvalue)
19 Int x(0);
20 Int y = ++x; // OK
21 // Int z = ++Int(1); // ERROR (not lvalue)
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 885
Move Semantics and the *this Parameter
1 #include <iostream>
2 #include <vector>
3 #include <utility>
4
5 class Buffer {
6 public:
7 Buffer(char value = 0) : data_(1024, value) {}
8 void data(std::vector<char>& x) const &
9 {x = data_;}
10 void data(std::vector<char>& x) &&
11 {x = std::move(data_);}
12 // ...
13 private:
14 std::vector<char> data_;
15 };
16
17 Buffer getBuffer() {return Buffer(42);}
18
19 int main() {
20 std::vector<char> d;
21 Buffer buffer;
22 buffer.data(d); // copy into d
23 getBuffer().data(d); // move into d
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 886
Section 3.4.6
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 887
References to References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 888
References to References (Continued)
Auto specifier:
int i = 0;
auto&& j = i; // reference to reference
Class templates:
template <class T>
struct Thing {
void func(T&&) {} // reference to reference
// if T is reference type
};
Thing<int&> x;
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 889
Reference Collapsing Rules
Let TR denote a type that is a reference to type T (where T may be cv
qualified).
The effect of reference collapsing is summarized below. .
Before Collapse After Collapse
TR& T&
const TR& T&
volatile TR& T&
const volatile TR& T&
TR&& TR
const TR&& TR
volatile TR&& TR
const volatile TR&& TR
In other words:
An lvalue reference to any reference yields an lvalue reference.
An rvalue reference to an lvalue reference yields an lvalue reference.
An rvalue reference to an rvalue reference yields rvalue reference.
Any cv qualifiers applied to a reference type are discarded (since cv
qualifiers cannot be applied to a reference).
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 890
Reference Collapsing Examples
Due to reference collapsing, T&& syntax may not always be an rvalue
reference. Example:
using IntRef = int&;
int i = 0;
IntRef&& r = i; // r is int& (i.e., lvalue reference)
Example:
using IntRef = int&;
using IntRefRef = int&&;
using ConstIntRefRef = const int&&;
using ConstIntRef = const int&;
using T1 = const IntRef &; // T1 is int&
using T2 = const IntRefRef &; // T2 is int&
using T3 = IntRefRef &&; // T3 is int&&
using T4 = ConstIntRef &&; // T4 is const int&
using T5 = ConstIntRefRef &&; // T5 is const int&&
Example:
int i = 0;
int& j = i;
auto&& k = j;
// j cannot be inferred to have type int
// since rvalue reference cannot be bound to lvalue
// j inferred to have type int&
// reference collapsing of int& && yields int&
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 891
Forwarding References
A cv-unqualified rvalue reference that appears in a type-deducing context
for template parameters is called a forwarding reference.
Type deduction for template parameters of template functions is defined in
such a way as to facilitate perfect forwarding.
Consider the following template-parameter type-deduction scenario:
template<class T>
void f(T&& p);
f(expr); // invoke f
Let E denote the type of the expression expr. The type T is then deduced
as follows:
1 If expr is an lvalue, T is deduced as E&, in which case the type of p yielded
E&&.
Thus, the type T&& will be an lvalue reference type if expr is an lvalue, and
an rvalue reference type if expr is an rvalue.
Therefore, the lvalue/rvalue-ness of expr can be determined inside f
based on whether T&& is an lvalue reference type or rvalue reference type.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 892
Forwarding References Example
1 #include <utility>
2
3 template <class T> void f(T&& p);
4 int main() {
5 int i = 42;
6 const int ci = i;
7 const int& rci = i;
8 f(i);
9 // i is lvalue with type int
10 // T is int&
11 // p has type int&
12 f(ci);
13 // ci is lvalue with type const int
14 // T is const int&
15 // p has type const int&
16 f(rci);
17 // rci is lvalue with type const int&
18 // T is const int&
19 // p has type const int&
20 f(2);
21 // 2 is rvalue with type int
22 // T is int
23 // p has type int&&
24 f(std::move(i));
25 // std::move(i) is rvalue with type int&&
26 // T is int
27 // p has type int&&
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 893
Section 3.4.7
Perfect Forwarding
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 894
Perfect Forwarding
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 895
Perfect-Forwarding Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 900
Perfect-Forwarding Example: Solution (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 901
The std::forward Template Function
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 903
Forwarding Example
1 #include <iostream>
2 #include <string>
3 #include <utility>
4
5 void func(std::string& s) {
6 std::cout << "func(std::string&) called\n";
7 }
8
9 void func(std::string&& s) {
10 std::cout << "func(std::string&&) called\n";
11 }
12
13 template <class T>
14 void wrapper(T&& x) {
15 func(std::forward<T>(x));
16 }
17
18 template <class T>
19 void buggy_wrapper(T x) {func(x);}
20
21 int main() {
22 using namespace std::literals;
23 std::string s("Hi"s);
24 wrapper(s); // which overload of func called?
25 buggy_wrapper(s); // which overload of func called?
26 wrapper("Hi"s); // which overload of func called?
27 buggy_wrapper("Hi"s); // which overload of func called?
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 904
Perfect-Forwarding Use Case: Wrapper Functions
A wrapper function is simply a function used to invoke another function,
possibly with some additional processing.
Example:
1 #include <iostream>
2 #include <utility>
3 #include <string>
4
5 std::string emphasize(const std::string& s)
6 {return s + "!";}
7
8 std::string emphasize(std::string&& s)
9 {return s + "!!!!";}
10
11 template <class A>
12 auto wrapper(A&& arg) {
13 std::cout << "Calling with argument " << arg << ’\n’;
14 auto result = emphasize(std::forward<A>(arg));
15 std::cout << "Return value " << result << ’\n’;
16 return result;
17 }
18
19 int main() {
20 std::string s("Bonjour");
21 wrapper(s);
22 wrapper(std::string("Hello"));
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 905
Perfect-Forwarding Use Case: Factory Functions
A factory function is simply a function used to create objects.
Often, perfect forwarding is used by factory functions in order to pass
arguments through to a constructor, which performs the actual object
creation.
Example:
1 #include <iostream>
2 #include <string>
3 #include <complex>
4 #include <utility>
5 #include <memory>
6
7 // Make an object of type T.
8 template<typename T, typename Arg>
9 std::shared_ptr<T> factory(Arg&& arg) {
10 return std::shared_ptr<T>(
11 new T(std::forward<Arg>(arg)));
12 }
13
14 int main() {
15 using namespace std::literals;
16 auto s(factory<std::string>("Hello"s));
17 auto z(factory<std::complex<double>>(1.0i));
18 std::cout << *s << ’ ’ << *z << ’\n’;
19 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 906
Perfect-Forwarding Use Case: Emplace Operations
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 907
Other Perfect-Forwarding Examples
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 908
Section 3.4.8
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 909
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 910
References II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 911
Talks I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 912
Section 3.5
Concurrency
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 913
Section 3.5.1
Preliminaries
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 914
Processors
Processor
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 916
Memory Hierarchy
Core L1 L2 LL Main Bulk
Excluding ···
Cache Cache Cache Cache Memory Storage
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 919
Why Multicore Processors?
in past, greater processing power obtained through higher clock rates
clock rates have stopped rising, topping out at about 5 GHz (little change
since about 2005)
power consumption is linear in clock frequency and quadratic in voltage,
but higher frequency typically requires higher voltage; so, considering
effect of frequency and voltage together, power consumption grows
approximately with cube of frequency
greater power consumption translates into increased heat production
higher clock rates would result in processors overheating
transistor counts still increasing (Moore’s law: since 1960s, transistor
count has doubled approximately every 18 months)
instead of increasing processing power by raising clock rate of processor
core, simply add more processor cores
n cores running at clock rate f use significantly less power and generate
less heat than single core at clock rate n f
going multicore allows for greater processing power with lower power
consumption and less heat production
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 920
Section 3.5.2
Multithreaded Programming
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 921
Concurrency
A thread is a sequence of instructions that can be independently
managed by the operating-system scheduler.
A process provides the resources that program needs to execute (e.g.,
address space, files, and devices) and at least one thread of execution.
All threads of a process share the same address space.
Concurrency is the situation where multiple threads execute over time
periods (i.e., from start of execution to end) that overlap (but no threads
are required to run simultaneously).
Parallelism refers to the situation where multiple threads execute
simultaneously.
Concurrency can be achieved with:
1 multiple single-threaded processes; or
2 a single multithreaded process.
A single multithreaded process is usually preferable, since this approach
is typically much less resource intensive and data can often be shared
much more easily between threads in a single process (due to the threads
having a common address space).
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 922
Why Multithreading?
Keep all of the processor cores busy (i.e., fully utilize all cores).
Most modern systems have multiple processor cores, due to having either
multiple processors or a single processor that is multicore.
A single thread cannot fully utilize the computational resources available in
such systems.
Keep processes responsive.
In graphics applications, keep the GUI responsive while the application is
performing slow operations such as I/O.
In network server applications, keep the server responsive to new
connections while handling already established ones.
Simplify the coding of cooperating tasks.
Some programs consist of several logically distinct tasks.
Instead of having the program manage when the computation associated
with different tasks is performed, each task can be placed in a separate
thread and the operating system can perform scheduling.
For certain types of applications, multithreading can significantly reduce the
conceptual complexity of the program.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 923
Section 3.5.3
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 924
Memory Model
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 925
Sequential Consistency (SC)
The environment in which a multithreaded program is run is said to have
sequential consistency (SC) if the result of any execution of the program
is the same as if the operations of all threads are executed in some
sequential order, and the operations of each individual thread appear in
this sequence in the order specified by the program.
In other words, in a sequentially-consistent execution of a multithreaded
program, threads behave as if their operations were simply interleaved.
Consider the multithreaded program (with two threads) shown below,
where x, y, a, and b are all integer variables and initially zero.
Thread 1 Code Thread 2 Code
x = 1; y = 1;
a = y; b = x;
Some sequentially-consistent executions of this program include:
x = 1; y = 1; b = x; a = y;
y = 1; x = 1; a = y; b = x;
x = 1; a = y; y = 1; b = x;
y = 1; b = x; x = 1; a = y;
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 926
Sequential-Consistency (SC) Memory Model
Suppose that the compiler makes the same optimization to the code for
thread 1 as on the previous slide, yielding the code below.
Optimized Thread 1 Code (Unchanged) Thread 2 Code
y = 1; if (y == 1) {
x = 1; assert(x == 1);
// ... }
Thread 2 can observe x and y being modified in the wrong order (i.e., an
order that is inconsistent with SC execution).
The assertion in thread 2 can never fail in the original program, but can
sometimes fail in the optimized program.
In a multithreaded program, the reordering of loads and stores must be
avoided if SC is to be maintained.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 929
Store-Buffer Example: Without Store Buffer
Consider the program below, where x, y, a, and b are integer variables, all
initially zero.
Thread 1 Code Thread 2 Code
x = 1; y = 1;
a = y; b = x;
y = 1; x = 1; a = y; b = x; (a is 1, b is 1)
x = 1; a = y; y = 1; b = x; (a is 0, b is 1)
y = 1; b = x; x = 1; a = y; (a is 1, b is 0)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 930
Store-Buffer Example: Store Buffer
Processor Memory
Register x
(2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 931
Store-Buffer Example: With Store Buffer (Not SC)
Core 1 Core 2 Memory
Code Store Buffer Code Store Buffer x y
x = 1; write 1 to x 0 0
pending
no change y = 1; write 1 to y 0 0
pending
a = y; no change no change 0 0
// a = 0;
no change b = x; no change 0 0
// b = 0;
write 1 to x no change 1 0
completed
write 1 to y 1 1
completed
The execution of the program results in a and b both being 0, which
violates SC.
The program behaves as if the lines of code in each thread were
reordered (i.e., reversed), yielding: a = y; b = x; x = 1; y = 1;.
A store buffer (or cache) must be avoided, if SC is to be maintained.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 932
Atomicity of Memory Operations
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 933
Data Races
If memory operations are not all atomic, the possibility exists for
something known as a data race.
Two memory operations are said to conflict if they access the same
memory location and at least one of the operations is a write.
Two conflicting memory operations form a data race if they are from
different threads and can be executed at the same time.
A program with data races usually has unpredictable behavior (e.g., due
to torn reads, torn writes, or worse).
Example (data race):
Consider the multithreaded program listed below, where x, y, and z are
(nonatomic) integer variables shared between threads and are initially zero.
Thread 1 Code Thread 2 Code
x = 1; y = 1;
a = y + z; b = x + z;
The program has data races on both x and y.
Since z is not modified by any thread, z cannot participate in a data race.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 934
Torn Reads
A torn read is a read operation that (due to lack of atomicity) has only
partially read its value when another (concurrent) write operation on the
same location is performed.
Consider a two-byte unsigned (big-endian) integer variable x, which is
initially 1234 (hexadecimal).
Suppose that the following (nonatomic) memory operations overlap in
time:
thread 1 reads x; and
A torn write is a write operation that (due to lack of atomicity) has only
partially written its value when another (concurrent) read or write
operation on the same location is performed.
Consider a two-byte unsigned (big-endian) integer variable x, which is
initially 0.
Suppose that the following (nonatomic) memory operations overlap in
time:
thread 1 writes 1234 (hexadecimal) to x; and
thread 2 writes 5678 (hexadecimal) to x.
Byte 0 Byte 1
Initially, x is 0: 00 00
Byte 0 Byte 1
Thread 1 writes 12 to the first byte of x, yielding: 12 00
Thread 2 writes 56 and 78 to the first and second bytes of x, respectively,
yielding: Byte
56
0 Byte 1
78
Byte 0 Byte 1
Thread 1 writes 34 to the second byte of x, yielding: 56 34
The resulting value in x (i.e., 5634) is neither the value written by thread 1
(i.e., 1234) nor the value written by thread 2 (i.e., 5678).
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 936
SC Data-Race Free (SC-DRF) Memory Model
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 938
Section 3.5.4
Thread Management
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 939
The std::thread Class
std::thread class provides means to create new thread of execution,
wait for thread to complete, and perform other operations to manage and
query state of thread
thread object may or may not be associated with thread (of execution)
thread object that is associated with thread said to be joinable
default constructor creates thread object that is unjoinable
can also construct thread object by providing callable entity (e.g.,
function or functor) and arguments (if any), resulting in new thread
invoking callable entity
thread function provided with copies of arguments so must use reference
wrapper class like std::reference_wrapper for reference semantics
thread class is movable but not copyable
each thread object has ID
IDs of joinable thread objects are unique
all unjoinable thread objects have same ID, distinct from ID of every
joinable thread object
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 940
The std::thread Class (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 941
std::thread Members
Member Types
Member Name Description
id thread ID type
native_handle_type system-dependent handle type for under-
lying thread entity
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 942
std::thread Members (Continued)
Member Functions
Member Name Description
joinable check if thread joinable
get_id get ID of thread
native_handle get native handle for thread
hardware_concurrency (static) get number of concurrent threads
supported by hardware
join wait for thread to finish executing
detach permit thread to execute indepen-
dently
swap swap threads
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 943
Example: Hello World With Threads
1 #include <iostream>
2 #include <thread>
3
4 void hello()
5 {
6 std::cout << "Hello World!\n";
7 }
8
9 int main()
10 {
11 std::thread t(hello);
12 t.join();
13 }
1 #include <iostream>
2 #include <thread>
3
4 int main()
5 {
6 std::thread t([](){
7 std::cout << "Hello World!\n";
8 });
9 t.join();
10 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 944
Example: Thread-Function Argument Passing (Copy Semantics)
1 #include <iostream>
2 #include <thread>
3
4 void doWork(int i, int j)
5 {
6 std::cout << i << ’ ’ << j << ’\n’;
7 }
8
9 int main()
10 {
11 int i = 42;
12 std::thread t1(doWork, i, 1);
13 t1.join();
14 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 945
Example: Thread-Function Argument Passing (Reference Semantics)
1 #include <iostream>
2 #include <vector>
3 #include <functional>
4 #include <thread>
5
6 void doWork(const std::vector<int>& v)
7 {
8 for (auto i : v) {
9 std::cout << i << ’\n’;
10 }
11 }
12
13 int main()
14 {
15 std::vector v{1, 2, 3, 4};
16
17 // copy semantics
18 std::thread t1(doWork, v);
19 t1.join();
20
21 // reference semantics
22 std::thread t2(doWork, std::ref(v));
23 t2.join();
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 946
Example: Thread-Function Argument Passing (Move Semantics)
1 #include <iostream>
2 #include <vector>
3 #include <utility>
4 #include <thread>
5
6 void doWork(std::vector<int>&& v)
7 {
8 for (auto i : v) {
9 std::cout << i << ’\n’;
10 }
11 }
12
13 int main()
14 {
15 std::vector v{1, 2, 3, 4};
16
17 // move semantics
18 std::thread t1(doWork, std::move(v));
19 t1.join();
20 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 947
Example: Moving Threads
1 #include <thread>
2 #include <iostream>
3 #include <utility>
4
5 // Return a thread that prints a greeting message.
6 std::thread makeThread() {
7 return std::thread([](){
8 std::cout << "Hello World!\n";
9 });
10 }
11
12 // Return the same thread that was passed as an argument.
13 std::thread identity(std::thread t) {
14 return t;
15 }
16
17 int main() {
18 std::thread t1(makeThread());
19 std::thread t2(std::move(t1));
20 t1 = std::move(t2);
21 t1 = identity(std::move(t1));
22 t1.join();
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 948
Example: Lifetime Bug
1 #include <iostream>
2 #include <vector>
3 #include <algorithm>
4 #include <chrono>
5 #include <thread>
6 #include <numeric>
7
8 void threadFunc(const std::vector<int>* v) {
9 std::cout << std::accumulate(v->begin(), v->end(), 0)
10 << ’\n’;
11 }
12
13 void startThread() {
14 std::vector<int> v(1000000, 1);
15 std::thread t(threadFunc, &v);
16 t.detach();
17 // v is destroyed here but detached thread
18 // may still be using v
19 }
20
21 int main() {
22 startThread();
23 // Give the thread started by startThread
24 // sufficient time to complete its work.
25 std::this_thread::sleep_for(std::chrono::seconds(5));
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 949
The std::this_thread Namespace
Name Description
get_id get ID of current thread
yield suggest rescheduling current thread so as to allow
other threads to run
sleep_for blocks execution of current thread for at least
specified duration
sleep_until blocks execution of current thread until specified
time reached
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 950
Example: Identifying Threads
1 #include <thread>
2 #include <iostream>
3
4 // main thread ID
5 std::thread::id mainThread;
6
7 void func() {
8 if (std::this_thread::get_id() == mainThread) {
9 std::cout << "called by main thread\n";
10 } else {
11 std::cout << "called by secondary thread\n";
12 }
13 }
14
15 int main() {
16 mainThread = std::this_thread::get_id();
17 std::thread t([](){
18 // call func from secondary thread
19 func();
20 });
21 // call func from main thread
22 func();
23 t.join();
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 951
Thread Local Storage
thread storage duration: object initialized before first use in thread and,
if constructed, destroyed on thread exit
each thread has its own instance of object
only objects declared thread_local have this storage duration
thread_local implies static for variable of block scope
thread_local can appear together with static or extern to
adjust linkage
example:
thread_local int counter = 0;
static thread_local int x = 0;
thread_local int y;
void func() {
thread_local int counter = 0;
// equivalent to:
// static thread_local int counter = 0;
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 952
Example: Thread Local Storage
1 #include <iostream>
2 #include <vector>
3 #include <thread>
4
5 thread_local int counter = 0;
6
7 void doWork(int id) {
8 static const char letters[] = "abcd";
9 for (int i = 0; i < 10; ++i) {
10 std::cout << letters[id] << counter << ’\n’;
11 ++counter;
12 }
13 }
14
15 int main() {
16 std::vector<std::thread> workers;
17 for (int i = 1; i <= 3; ++i) {
18 // invoke doWork in new thread
19 workers.emplace_back(doWork, i);
20 }
21 // invoke doWork in main thread
22 doWork(0);
23 for (auto& t : workers) {t.join();}
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 953
The std::thread Class and Exception Safety
The astute reader will notice that most code examples on these lecture
slides (both earlier and later) that directly employ std::thread are not
exception safe.
Some of the exception safety problems in these examples could be
eliminated by using a RAII class to wrap std::thread objects.
Unfortunately, the standard library does not provide such a RAII class.
At a very basic level, one could provide a thread wrapper class that has
similar functionality to std::thread, except that its destructor
automatically joins with the underlying thread if the thread is still joinable
at destruction time. (See next slide.)
Although such an approach will work in some situations (such as in the
case of many of the simple code examples on these lecture slides), it can
potentially lead to deadlocks and other problems in more complex code.
A more general solution would be to provide a class that allows arbitrary
code to be executed just prior to thread destruction, in order to perform
the appropriate (application-dependent) “clean-up” action. (For example,
see boost::scoped_thread in the Boost Threads library.)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 954
The std::thread Class and Exception Safety (Continued)
1 #include <thread>
2
3 // A minimalist inheritance-based replacement for std::thread
4 // that joins automatically in the destructor.
5 // (One must be careful not to use this type polymorphically
6 // since the destructor is not virtual.)
7 class scoped_thread : public std::thread {
8 public:
9 using std::thread::thread;
10 scoped_thread(scoped_thread&&) = default;
11 scoped_thread& operator=(scoped_thread&&) = default;
12 scoped_thread(const scoped_thread&) = delete;
13 scoped_thread& operator=(const scoped_thread&) = delete;
14 ˜scoped_thread() {
15 if (joinable()) {
16 join();
17 }
18 }
19 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 955
Section 3.5.5
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 956
Shared Data
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 958
Critical Sections
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 959
Data-Race Example
above code has data race on balance object (i.e., more than one thread
may access balance at same time with at least one thread writing)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 960
Example: Data Race (Counter)
1 #include <iostream>
2 #include <thread>
3
4 unsigned long long counter = 0;
5
6 void func() {
7 for (int i = 0; i < 1000000; ++i) {
8 ++counter;
9 }
10 }
11
12 int main() {
13 std::thread t1(func);
14 std::thread t2(func);
15 t1.join();
16 t2.join();
17 std::cout << counter << ’\n’;
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 961
Example: Data Race and/or Race Condition (IntSet)
1 #include <thread>
2 #include <iostream>
3 #include <set>
4
5 class IntSet {
6 public:
7 bool contains(int i) const
8 {return s_.find(i) != s_.end();}
9 void add(int i)
10 {s_.insert(i);}
11 private:
12 std::set<int> s_;
13 };
14
15 IntSet s;
16
17 int main() {
18 std::thread t1([](){
19 for (int i = 0; i < 1000; ++i) s.add(2 * i);
20 });
21 std::thread t2([](){
22 for (int i = 0; i < 1000; ++i) s.add(2 * i + 1);
23 });
24 t1.join(); t2.join();
25 std::cout << s.contains(1000) << ’\n’;
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 962
Section 3.5.6
Mutexes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 963
Mutexes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 964
The std::mutex Class
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 965
std::mutex Members
Member Types
Name Description
native_handle_type system-dependent handle type for underlying mu-
tex entity
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 966
Example: Avoiding Data Race Using Mutex (Counter) (mutex)
1 #include <iostream>
2 #include <thread>
3 #include <mutex>
4
5 std::mutex m;
6 unsigned long long counter = 0;
7
8 void func() {
9 for (int i = 0; i < 1000000; ++i) {
10 m.lock(); // acquire mutex
11 ++counter;
12 m.unlock(); // release mutex
13 }
14 }
15
16 int main() {
17 std::thread t1(func);
18 std::thread t2(func);
19 t1.join();
20 t2.join();
21 std::cout << counter << ’\n’;
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 967
The std::lock_guard Template Class
std::lock_guard is RAII class for mutexes
declaration:
template <class T> class lock_guard;
template parameter T specifies type of mutex (e.g., std::mutex,
std::recursive_mutex)
avoids problem of inadvertently forgetting to release mutex (e.g., due to
exception or forgetting unlock call)
constructor takes mutex as argument
not movable and not copyable
acquires mutex in constructor
releases mutex in destructor
since language ensures that all objects destroyed at end of lifetime,
release of mutex guaranteed (even if some code skipped due to thrown
exception)
advisable to use lock_guard instead of calling lock and unlock
explicitly
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 968
std::lock_guard Members
Member Types
Name Description
mutex_type underlying mutex type
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 969
Example: Avoiding Data Race Using Mutex (Counter) (lock_guard)
1 #include <iostream>
2 #include <thread>
3 #include <mutex>
4
5 std::mutex m;
6 unsigned long long counter = 0;
7
8 void func() {
9 for (int i = 0; i < 1000000; ++i) {
10 // lock_guard constructor acquires mutex
11 std::lock_guard lock(m);
12 ++counter;
13 // lock_guard destructor releases mutex
14 }
15 }
16
17 int main() {
18 std::thread t1(func);
19 std::thread t2(func);
20 t1.join();
21 t2.join();
22 std::cout << counter << ’\n’;
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 970
Example: Avoiding Data Race Using Mutex (IntSet) (lock_guard)
1 #include <thread>
2 #include <iostream>
3 #include <set>
4 #include <mutex>
5
6 class IntSet {
7 public:
8 bool contains(int i) const {
9 std::lock_guard lg(m_);
10 return s_.find(i) != s_.end();
11 }
12 void add(int i) {
13 std::lock_guard lg(m_);
14 s_.insert(i);
15 }
16 private:
17 std::set<int> s_;
18 mutable std::mutex m_;
19 };
20
21 IntSet s;
22
23 int main() {
24 std::thread t1([](){
25 for (int i = 0; i < 1000; ++i) s.add(2 * i);
26 });
27 std::thread t2([](){
28 for (int i = 0; i < 1000; ++i) s.add(2 * i + 1);
29 });
30 t1.join(); t2.join();
31 std::cout << s.contains(1000) << ’\n’;
32 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 971
The std::scoped_lock Template Class
std::scoped_lock is RAII class for mutexes
declaration:
template <class... Ts> class scoped_lock;
parameter pack Ts specifies types of mutexes to be locked
can be used with any mutex types providing necessary locking interface
(e.g., std::mutex and std::recursive_mutex)
constructor takes one or more mutexes as arguments
mutexes acquired in constructor and released in destructor
scoped_lock objects are not movable and not copyable
using scoped_lock avoids problem of inadvertently failing to release
mutexes (e.g., due to exception or forgetting unlock calls)
in multiple mutex case, employs deadlock avoidance algorithm from
std::lock (discussed later) when acquiring mutexes
advisable to use scoped_lock instead of calling lock and unlock
explicitly
scoped_lock effectively replaces (and extends) lock_guard
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 972
Example: Avoiding Data Race Using Mutex (IntSet) (scoped_lock)
1 #include <thread>
2 #include <iostream>
3 #include <unordered_set>
4 #include <mutex>
5
6 class IntSet {
7 public:
8 bool contains(int i) const {
9 std::scoped_lock lock(m_);
10 return s_.find(i) != s_.end();
11 }
12 void add(int i) {
13 std::scoped_lock lock(m_);
14 s_.insert(i);
15 }
16 private:
17 std::unordered_set<int> s_;
18 mutable std::mutex m_;
19 };
20
21 IntSet s;
22
23 int main() {
24 std::thread t1([](){
25 for (int i = 0; i < 10’000; ++i) {s.add(2 * i);}
26 });
27 std::thread t2([](){
28 for (int i = 0; i < 10’000; ++i) {s.add(2 * i + 1);}
29 });
30 t1.join(); t2.join();
31 std::cout << s.contains(1000) << ’\n’;
32 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 973
The std::unique_lock Template Class
std::unique_lock is another RAII class for mutexes
declaration:
template <class T> class unique_lock;
template parameter T specifies type of mutex (e.g., std::mutex,
std::recursive_mutex)
unlike case of std::lock_guard, in case of unique_lock do not have to
hold mutex over entire lifetime of RAII object
have choice of whether to acquire mutex upon construction
also can acquire and release mutex many times throughout lifetime of
unique_lock object
upon destruction, if mutex is held, it is released
since mutex is always guaranteed to be released by destructor, cannot
forget to release mutex
unique_lock is used in situations where want to be able to transfer
ownership of lock (e.g., return from function) or RAII object needed for
mutex but do not want to hold mutex over entire lifetime of RAII object
movable but not copyable
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 974
std::unique_lock Members
Member Types
Name Description
mutex_type underlying mutex type
Locking Functions
Name Description
lock acquire mutex, blocking if not available
try_lock try to lock mutex without blocking
try_lock_for try to lock mutex without blocking
try_lock_until try to lock mutex without blocking
unlock release mutex
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 975
std::unique_lock Members (Continued)
Observer Functions
Name Description
owns_lock tests if lock owns associated mutex
operator bool tests if lock owns associated mutex
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 976
Example: Avoiding Data Race Using Mutex (Counter) (unique_lock)
1 #include <iostream>
2 #include <thread>
3 #include <mutex>
4
5 std::mutex m;
6 unsigned long long counter = 0;
7
8 void func() {
9 for (int i = 0; i < 1000000; ++i) {
10 // Create a lock object without locking the mutex.
11 std::unique_lock lock(m, std::defer_lock);
12 // ...
13 // Lock the mutex.
14 lock.lock();
15 ++counter;
16 // The unique_lock destructor releases the mutex.
17 }
18 }
19
20 int main() {
21 std::thread t1(func);
22 std::thread t2(func);
23 t1.join();
24 t2.join();
25 std::cout << counter << ’\n’;
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 977
The std::lock Template Function
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 978
Example: Acquiring Two Locks for Swap (Incorrect)
1 #include <thread>
2 #include <vector>
3 #include <mutex>
4
5 class BigBuf // A Big Buffer
6 {
7 public:
8 static constexpr int size() {return 16 * 1024 * 1024;}
9 BigBuf() : data_(size()) {}
10 BigBuf& operator=(const BigBuf&) = delete;
11 BigBuf& operator=(BigBuf&&) = delete;
12 void swap(BigBuf& other) {
13 if (this == &other)
14 return;
15 // acquiring the two mutexes in this way can result in deadlock
16 std::lock_guard lock1(m_);
17 std::lock_guard lock2(other.m_);
18 std::swap(data_, other.data_);
19 }
20 // ...
21 private:
22 std::vector<char> data_;
23 mutable std::mutex m_;
24 };
25
26 BigBuf a;
27 BigBuf b;
28
29 int main()
30 {
31 std::thread t1([](){
32 for (int i = 0; i < 100000; ++i) a.swap(b);
33 });
34 std::thread t2([](){
35 for (int i = 0; i < 100000; ++i) b.swap(a);
36 });
37 t1.join(); t2.join();
38 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 979
Example: Acquiring Two Locks for Swap [unique_lock and lock]
1 #include <mutex>
2 #include <thread>
3 #include <utility>
4 #include <vector>
5
6 class BigBuf // A Big Buffer
7 {
8 public:
9 static constexpr int size() {return 16 * 1024 * 1024;}
10 BigBuf() : data_(size()) {}
11 BigBuf& operator=(const BigBuf&) = delete;
12 BigBuf& operator=(BigBuf&&) = delete;
13 void swap(BigBuf& other) {
14 if (this == &other)
15 return;
16 std::unique_lock lock1(m_, std::defer_lock);
17 std::unique_lock lock2(other.m_, std::defer_lock);
18 std::lock(lock1, lock2);
19 std::swap(data_, other.data_);
20 }
21 // ...
22 private:
23 std::vector<char> data_;
24 mutable std::mutex m_;
25 };
26
27 BigBuf a;
28 BigBuf b;
29
30 int main() {
31 std::thread t1([](){
32 for (int i = 0; i < 100000; ++i) a.swap(b);
33 });
34 std::thread t2([](){
35 for (int i = 0; i < 100000; ++i) b.swap(a);
36 });
37 t1.join(); t2.join();
38 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 980
Example: Acquiring Two Locks for Swap [scoped_lock]
1 #include <mutex>
2 #include <thread>
3 #include <utility>
4 #include <vector>
5
6 class BigBuf // A Big Buffer
7 {
8 public:
9 static constexpr int size() {return 16 * 1024 * 1024;}
10 BigBuf() : data_(size()) {}
11 BigBuf& operator=(const BigBuf&) = delete;
12 BigBuf& operator=(BigBuf&&) = delete;
13 void swap(BigBuf& other) {
14 if (this == &other)
15 return;
16 std::scoped_lock sl(m_, other.m_);
17 std::swap(data_, other.data_);
18 }
19 // ...
20 private:
21 std::vector<char> data_;
22 mutable std::mutex m_;
23 };
24
25 BigBuf a;
26 BigBuf b;
27
28 int main() {
29 std::thread t1([](){
30 for (int i = 0; i < 100000; ++i) a.swap(b);
31 });
32 std::thread t2([](){
33 for (int i = 0; i < 100000; ++i) b.swap(a);
34 });
35 t1.join(); t2.join();
36 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 981
The std::timed_mutex Class
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 982
Example: Acquiring Mutex With Timeout (std::timed_mutex)
1 #include <vector>
2 #include <iostream>
3 #include <thread>
4 #include <mutex>
5 #include <chrono>
6
7 std::timed_mutex m;
8
9 void doWork() {
10 for (int i = 0; i < 10000; ++i) {
11 std::unique_lock lock(m, std::defer_lock);
12 int count = 0;
13 while (!lock.try_lock_for(
14 std::chrono::microseconds(1))) {++count;}
15 std::cout << count << ’\n’;
16 }
17 }
18
19 int main() {
20 std::vector<std::thread> workers;
21 for (int i = 0; i < 16; ++i) {
22 workers.emplace_back(doWork);
23 }
24 for (auto& t : workers) {t.join();}
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 983
Recursive Mutexes
A recursive mutex is a mutex for which a thread may own multiple locks
at the same time.
After a mutex is first locked by thread A, thread A can acquire additional
locks on the mutex (without releasing the lock already held).
The mutex is not available to other threads until thread A releases all of its
locks on the mutex.
A recursive mutex is typically used when code that locks a mutex must call
other code that locks the same mutex (in order to avoid deadlock).
For example, a function that acquires a mutex and recursively calls itself
(resulting in the mutex being relocked) would need to employ a recursive
mutex.
A recursive mutex has more overhead than a nonrecursive mutex.
Code that uses recursive mutexes can often be more difficult to
understand and therefore more prone to bugs.
Consequently, the use of recursive mutexes should be avoided if possible.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 984
Recursive Mutex Classes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 985
Shared Mutexes
A shared mutex (also known as a multiple-reader/single-writer mutex)
is a mutex that allows both shared and exclusive access.
A shared mutex has two types of locks: shared and exclusive.
Exclusive lock:
Only one thread can hold an exclusive lock on a mutex.
While a thread holds an exclusive lock on a mutex, no other thread can hold
any type of lock on the mutex.
Shared lock:
Any number of threads (within implementation limits) can take a shared
lock on a mutex.
While any thread holds a shared lock on a mutex, no thread may take an
exclusive lock on the mutex.
A shared mutex would typically be used to protect shared data that is
seldom updated but cannot be safely updated if any thread is reading it.
A thread takes a shared lock for reading, thus allowing multiple readers.
A thread takes an exclusive lock for writing, thus allowing only one writer
with no readers.
A shared mutex need not be fair in its granting of locks (e.g., readers
could starve writers).
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 986
The std::shared_mutex Class
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 987
std::shared_mutex Members
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 988
std::shared_mutex Members (Continued)
Other Functions
Name Description
native_handle get handle for underlying mutex entity
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 989
The std::shared_lock Template Class
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 990
Example: std::shared_mutex
1 #include <thread>
2 #include <mutex>
3 #include <iostream>
4 #include <vector>
5 #include <shared_mutex>
6
7 std::mutex coutMutex;
8 int counter = 0;
9 std::shared_mutex counterMutex;
10
11 void writer() {
12 for (int i = 0; i < 10; ++i) {
13 {
14 std::lock_guard lock(counterMutex);
15 ++counter;
16 }
17 std::this_thread::sleep_for(std::chrono::milliseconds(100));
18 }
19 }
20
21 void reader() {
22 for (int i = 0; i < 100; ++i) {
23 int c;
24 {
25 std::shared_lock lock(counterMutex);
26 c = counter;
27 }
28 {
29 std::lock_guard lock(coutMutex);
30 std::cout << std::this_thread::get_id() << ’ ’ << c << ’\n’;
31 }
32 std::this_thread::sleep_for(std::chrono::milliseconds(10));
33 }
34 }
35
36 int main() {
37 std::vector<std::thread> threads;
38 threads.emplace_back(writer);
39 for (int i = 0; i < 16; ++i) threads.emplace_back(reader);
40 for (auto& t : threads) t.join();
41 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 991
The std::shared_timed_mutex Class
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 992
Example: std::shared_timed_mutex
1 #include <thread>
2 #include <mutex>
3 #include <iostream>
4 #include <vector>
5 #include <shared_mutex>
6
7 std::mutex coutMutex;
8 int counter = 0;
9 std::shared_timed_mutex counterMutex;
10
11 void writer() {
12 for (int i = 0; i < 10; ++i) {
13 {
14 std::lock_guard lock(counterMutex);
15 ++counter;
16 }
17 std::this_thread::sleep_for(std::chrono::milliseconds(100));
18 }
19 }
20
21 void reader() {
22 for (int i = 0; i < 100; ++i) {
23 int c;
24 {
25 std::shared_lock lock(counterMutex);
26 c = counter;
27 }
28 {
29 std::lock_guard lock(coutMutex);
30 std::cout << std::this_thread::get_id() << ’ ’ << c << ’\n’;
31 }
32 std::this_thread::sleep_for(std::chrono::milliseconds(10));
33 }
34 }
35
36 int main() {
37 std::vector<std::thread> threads;
38 threads.emplace_back(writer);
39 for (int i = 0; i < 16; ++i) threads.emplace_back(reader);
40 for (auto& t : threads) t.join();
41 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 993
std::once_flag and std::call_once
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 994
Example: One-Time Action
1 #include <iostream>
2 #include <vector>
3 #include <thread>
4 #include <mutex>
5
6 std::once_flag flag;
7
8 void worker(int id) {
9 std::call_once(flag, [id](){
10 // This code will be invoked only once.
11 std::cout << "first: " << id << ’\n’;
12 });
13 }
14
15 int main() {
16 std::vector<std::thread> threads;
17 for (int i = 0; i < 16; ++i) {
18 threads.emplace_back(worker, i);
19 }
20 for (auto& t : threads) {
21 t.join();
22 }
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 995
Example: One-Time Initialization
1 #include <vector>
2 #include <thread>
3 #include <mutex>
4 #include <cassert>
5 #include <memory>
6
7 std::unique_ptr<int> value;
8 std::once_flag initFlag;
9
10 void initValue() {value = std::make_unique<int>(42);}
11
12 const int& getValue() {
13 std::call_once(initFlag, initValue);
14 return *value.get();
15 }
16
17 void doWork() {
18 const int& v = getValue();
19 assert(v == 42);
20 // ...
21 }
22
23 int main() {
24 std::vector<std::thread> threads;
25 for (int i = 0; i < 4; ++i)
26 {threads.emplace_back(doWork);}
27 for (auto& t : threads) {t.join();}
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 996
Static Local Variable Initialization and Thread Safety
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 997
Section 3.5.7
Condition Variables
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 998
Condition Variables
In concurrent programs, the need often arises for a thread to wait until a
particular event occurs (e.g., I/O has completed or data is available).
Having a thread repeatedly check for the occurrence of an event can be
inefficient (i.e., can waste processor resources).
It is often better to have the thread block and then only resume execution
after the event of interest has occurred.
A condition variable is a synchronization primitive that allows threads to
wait (by blocking) until a particular condition occurs.
A condition variable corresponds to some event of interest.
A thread that wants to wait for an event, performs a wait operation on the
condition variable.
A thread that wants to notify one or more waiting threads of an event
performs a signal operation on the condition variable.
When a signalled thread resumes, however, the signalled condition is not
guaranteed to be true (and must be rechecked), since another thread may
have caused condition to change.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 999
The std::condition_variable Class
Member Types
Name Description
native_handle_type system-dependent handle type for underlying con-
dition variable entity
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1001
std::condition_variable Members (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1002
Example: Condition Variable (IntStack)
1 #include <iostream>
2 #include <vector>
3 #include <thread>
4 #include <mutex>
5 #include <condition_variable>
6
7 class IntStack {
8 public:
9 IntStack() {};
10 IntStack(const IntStack&) = delete;
11 IntStack& operator=(const IntStack&) = delete;
12 int pop() {
13 std::unique_lock lock(m_);
14 c_.wait(lock, [this](){return !v_.empty();});
15 int x = v_.back();
16 v_.pop_back();
17 return x;
18 }
19 void push(int x) {
20 std::lock_guard lock(m_);
21 v_.push_back(x);
22 c_.notify_one();
23 }
24 private:
25 std::vector<int> v_;
26 mutable std::mutex m_;
27 mutable std::condition_variable c_; // not empty
28 };
29
30 constexpr int numIters = 1000;
31 IntStack s;
32
33 int main() {
34 std::thread t1([](){
35 for (int i = 0; i < numIters; ++i) s.push(2 * i + 1);
36 });
37 std::thread t2([](){
38 for (int i = 0; i < numIters; ++i) std::cout << s.pop() << ’\n’;
39 });
40 t1.join(); t2.join();
41 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1003
The std::condition_variable_any Class
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1004
Section 3.5.8
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1005
Promises and Futures
promise and future together form one-time communication channel for
passing result (i.e., value or exception) of computation from one thread to
same or another thread
promise: object associated with promised result (i.e., value or exception)
to be produced
future: object through which promised result later made available
shared state: holds promised result for access through future object
(shared by promise object and corresponding future object)
producer of result uses promise object to store result in shared state
consumer uses future object (corresponding to promise) to retrieve result
from shared state
Producer Consumer
Promise Future
Shared
State
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1006
Promises and Futures (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1007
The std::promise Template Class
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1009
std::promise Members (Continued)
Other Functions
Name Description
swap swap two promise objects
get_future get future associated with promised
result
set_value set result to specified value
set_value_at_thread_exit set result to specified value while de-
livering notification only at thread exit
set_exception set result to specified exception
set_exception_at_thread_exit set result to specified exception while
delivering notification only at thread
exit
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1010
The std::future Template Class
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1011
std::future Members
Other Functions
Name Description
share transfer shared state to shared_future object
get get result
valid check if future object refers to shared state
wait wait for result to become available
wait_for wait for result to become available or time duration to expire
wait_until wait for result to become available or time point to be
reached
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1012
Example: Promises and Futures (Without std::async)
1 #include <future>
2 #include <thread>
3 #include <iostream>
4 #include <utility>
5
6 double computeValue() {
7 return 42.0;
8 }
9
10 void produce(std::promise<double> p) {
11 // write result to promise
12 p.set_value(computeValue());
13 }
14
15 int main() {
16 std::promise<double> p;
17 auto f = p.get_future(); // save future before move
18 std::thread producer(produce, std::move(p));
19 std::cout << f.get() << ’\n’;
20 producer.join();
21 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1013
The std::shared_future Template Class
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1014
Example: std::shared_future
1 #include <iostream>
2 #include <vector>
3 #include <thread>
4 #include <future>
5
6 void consume(std::shared_future<int> f) {
7 std::cout << f.get() << ’\n’;
8 }
9
10 int main() {
11 std::promise<int> p;
12 std::shared_future f = p.get_future().share();
13 std::vector<std::thread> consumers;
14 for (int i = 0; i < 16; ++i) {
15 consumers.emplace_back(consume, f);
16 }
17 p.set_value(42);
18 for (auto& i : consumers) {
19 i.join();
20 }
21 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1015
The std::async Template Function
std::async template function used to launch callable entity (e.g.,
function or functor) asynchronously
declaration (uses default launch policy):
template <class Func, class... Args>
future<typename result_of<typename decay<Func>::type(
typename decay<Args>::type...)>::type>
async(Func&& f, Args&&... args);
declaration (with launch policy parameter):
template <class Func, class... Args>
future<typename result_of<typename decay<Func>::type(
typename decay<Args>::type...)>::type>
async(launch policy, Func&& f, Args&&... args);
numerous launch policies supported via bitmask std::launch
if async bit set, execute on new thread
if deferred bit set, execute on calling thread when result needed
if multiple bits set, implementation free to choose between them
in asynchronous execution case, essentially creates promise to hold result
and returns associated future; launches thread to execute function/functor
and sets promise when function/functor returns
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1016
The std::async Template Function (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1017
Example: Promises and Futures (With std::async)
1 #include <future>
2 #include <iostream>
3
4 double computeValue() {
5 return 42.0;
6 }
7
8 int main() {
9 // invoke computeValue function asynchronously in
10 // separate thread
11 auto f = std::async(std::launch::async, computeValue);
12 std::cout << f.get() << ’\n’;
13 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1018
Example: Futures and Exceptions
1 #include <iostream>
2 #include <vector>
3 #include <cmath>
4 #include <future>
5 #include <stdexcept>
6
7 double squareRoot(double x) {
8 if (x < 0.0) {
9 throw std::domain_error(
10 "square root of negative number");
11 }
12 return std::sqrt(x);
13 }
14
15 int main() {
16 std::vector values{1.0, 2.0, -1.0};
17 std::vector<std::future<double>> results;
18 for (auto x : values) {
19 results.push_back(std::async(squareRoot, x));
20 }
21 for (auto& x : results) {
22 try {
23 std::cout << x.get() << ’\n’;
24 } catch (const std::domain_error&) {
25 std::cout << "error\n";
26 }
27 }
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1019
The std::packaged_task Template Class
template parameters R and Args specify return type and arguments for
callable entity
similar to std::function except return value of wrapped function made
available via future
packaged task often used as thread function
movable but not copyable
get_future member retrieves future associated with packaged task
get_future can be called only once
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1020
std::packaged_task Members
Construction, Destruction, and Assignment
Name Description
constructor construct object
destructor destroy object
operator= move assignment
Other Functions
Name Description
valid check if task object currently associated
with shared state
swap swap two task objects
get_future get future associated with promised result
operator() invoke function
make_ready_at_thread_exit invoke function ensuring result ready only
once current thread exits
reset reset shared state, abandoning any previ-
ously stored result
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1021
Example: Packaged Task
1 #include <iostream>
2 #include <thread>
3 #include <future>
4 #include <utility>
5 #include <chrono>
6
7 int getMeaningOfLife() {
8 // Let the suspense build before providing the answer.
9 std::this_thread::sleep_for(std::chrono::milliseconds(
10 1000));
11 // Return the answer.
12 return 42;
13 }
14
15 int main() {
16 std::packaged_task<int()> pt(getMeaningOfLife);
17 // Save the future.
18 auto f = pt.get_future();
19 // Start a thread running the task and detach the thread.
20 std::thread t(std::move(pt));
21 t.detach();
22 // Get the result via the future.
23 int result = f.get();
24 std::cout << "The meaning of life is " << result << ’\n’;
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1022
Example: Packaged Task With Arguments
1 #include <iostream>
2 #include <cmath>
3 #include <thread>
4 #include <future>
5
6 double power(double x, double y) {
7 return std::pow(x, y);
8 }
9
10 int main() {
11 // invoke task in main thread
12 std::packaged_task<double(double, double)> task(power);
13 task(0.5, 2.0);
14 std::cout << task.get_future().get() << ’\n’;
15 // reset shared state
16 task.reset();
17 // invoke task in new thread
18 auto f = task.get_future();
19 std::thread t(std::move(task), 2.0, 0.5);
20 t.detach();
21 std::cout << f.get() << ’\n’;
22 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1023
Section 3.5.9
Atomics
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1024
Atomics
These types provide a uniform interface for accessing the atomic memory
operations of the underlying hardware.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1025
Atomics (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1026
The std::atomic_flag Class
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1027
std::atomic_flag Members
Member Functions
Member Name Description
constructor constructs object
clear atomically sets flag to false
test_and_set atomically sets flag to true and obtains its pre-
vious value
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1028
Example: Suboptimal Spinlock Mutex
1 #include <iostream>
2 #include <thread>
3 #include <atomic>
4 #include <mutex>
5
6 class SpinLockMutex {
7 public:
8 SpinLockMutex() {f_.clear();}
9 void lock() {while (f_.test_and_set()) {}}
10 void unlock() {f_.clear();}
11 private:
12 std::atomic_flag f_; // true if thread holds mutex
13 };
14
15 SpinLockMutex m;
16 unsigned long long counter = 0;
17
18 void doWork() {
19 for (unsigned long long i = 0; i < 100000ULL; ++i)
20 {std::lock_guard lock(m); ++counter;}
21 }
22
23 int main() {
24 std::thread t1(doWork), t2(doWork);
25 t1.join(); t2.join();
26 std::cout << counter << ’\n’;
27 }
default memory order is suboptimal (and will be revisited later)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1029
Example: One-Time Wait
1 #include <iostream>
2 #include <atomic>
3 #include <thread>
4 #include <chrono>
5
6 // notReady flag initially not set
7 std::atomic_flag notReady = ATOMIC_FLAG_INIT;
8 int result = 0;
9
10 int main() {
11 notReady.test_and_set(); // indicate result not ready
12 std::thread producer([](){
13 std::this_thread::sleep_for(std::chrono::seconds(1));
14 result = -42;
15 notReady.clear(); // indicate result ready
16 });
17 std::thread consumer([](){
18 // loop until result ready
19 while (notReady.test_and_set()) {}
20 std::cout << result << ’\n’;
21 });
22 producer.join();
23 consumer.join();
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1030
The std::atomic Template Class
Basic
Member Name Description
constructor constructs object
operator= atomically store value into atomic object
is_lock_free check if atomic object is lock free
store atomically replaces value of atomic object
with given value
load atomically reads value of atomic object
operator T obtain result of load
exchange atomically replaces value of atomic object
with given value and obtain value of previous
value
compare_exchange_weak similar to exchange_strong but may fail spu-
riously
compare_exchange_strong atomically compare value of atomic object to
given value and perform exchange if equal or
load otherwise
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1032
std::atomic Members (Continued 1)
Fetch
Member Name Description
fetch_add atomically adds given value to value stored in atomic object
and obtains value held previously
fetch_sub atomically subtracts given value from value stored in atomic
object and obtains value held previously
fetch_and atomically replaces value of atomic object with bitwise AND
of atomic object’s value and given value, and obtains value
held previously
fetch_or atomically replaces value of atomic object with bitwise OR
of atomic object’s value and given value, and obtains value
held previously
fetch_xor atomically replaces value of atomic object with bitwise XOR
of atomic object’s value and given value, and obtains value
held previously
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1033
std::atomic Members (Continued 2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1034
std::atomic Members (Continued 3)
Compound Assignment
Member Name Description
operator+= atomically adds given value to value stored in atomic
object
operator-= atomically subtracts given value from value stored in
atomic object
operator&= atomically performs bitwise AND of given value with
value stored in atomic object
operator|= atomically performs bitwise OR of given value with
value stored in atomic object
operatorˆ= atomically performs bitwise XOR of given value with
value stored in atomic object
Constants
Member Name Description
is_always_lock_free indicates if type always lock free
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1035
Example: Atomic Counter
1 #include <iostream>
2 #include <vector>
3 #include <thread>
4 #include <atomic>
5
6 class AtomicCounter {
7 public:
8 AtomicCounter() : c_(0) {}
9 int operator++() {return ++c_;}
10 int get() const {return c_.load();}
11 private:
12 std::atomic<int> c_;
13 };
14
15 AtomicCounter counter;
16
17 void doWork() {
18 for (int i = 0; i < 10000; ++i) {++counter;}
19 }
20
21 int main() {
22 std::vector<std::thread> v;
23 for (int i = 0; i < 10; ++i)
24 {v.emplace_back(doWork);}
25 for (auto& t : v) {t.join();}
26 std::cout << counter.get() << ’\n’;
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1036
Example: Atomic Increment With Compare and Swap
1 #include <atomic>
2
3 template <class T>
4 void atomicIncrement(std::atomic<T>& x) {
5 T curValue = x;
6 while (!x.compare_exchange_weak(curValue,
7 curValue + 1)) {}
8 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1037
Example: Counting Contest
1 #include <iostream>
2 #include <vector>
3 #include <atomic>
4 #include <thread>
5
6 constexpr int numThreads = 10;
7 std::atomic ready(false);
8 std::atomic done(false);
9 std::atomic startCount(0);
10
11 void doCounting(int id) {
12 ++startCount;
13 while (!ready) {}
14 for (volatile int i = 0; i < 20000; i++) {}
15 bool expected = false;
16 if (done.compare_exchange_strong(expected, true))
17 {std::cout << "winner: " << id << ’\n’;}
18 }
19
20 int main() {
21 std::vector<std::thread> threads;
22 for (int i = 0; i < numThreads; ++i)
23 {threads.emplace_back(doCounting, i);}
24 while (startCount != numThreads) {}
25 ready = true;
26 for (auto& t : threads) {t.join();}
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1038
An Obligatory Note on volatile
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1039
Section 3.5.10
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1040
Semantics of Multithreaded Programs
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1041
Happens-Before Relationships
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1045
Inter-Thread Happens-Before Relationships
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1046
Summary of Happens-Before Relationships
happens before) B; or
2 A and B are in different threads and A inter-thread happens before B.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1047
Synchronizes-With Relationships
Suppose that the call of the function foo is known to synchronize with the
return from the function bar, which implies that A synchronizes with B.
Since A synchronizes with B, A must inter-thread happen before B, which
implies that A happens before B.
Therefore, the assertion in thread 2 can never fail.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1048
Examples of Synchronizes-With Relationships
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1049
Synchronizes-With Relationship: Thread Create and Join
1 #include <thread>
2 #include <cassert>
3
4 int x = 0;
5
6 void doWork() {
7 // A1 (start of thread execution)
8 assert(x == 1); // OK: M1 synchronizes with A1
9 x = 2;
10 // A2 (end of thread execution)
11 }
12
13 int main() {
14 x = 1;
15 std::thread t(doWork); // M1 (completion of constructor)
16 t.join(); // M2 (return from join)
17 assert(x == 2); // OK: A2 synchronizes with M2
18 }
since construction of thread (M1) synchronizes with start of thread function
execution (A1), M1 happens before A1 implying that assertion in doWork
cannot fail
since completion of execution of thread function (A2) synchronizes with
join operation (M2), A2 happens before M2 implying that assertion in
main cannot fail
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1050
Synchronizes-With Relationship: Mutex Lock/Unlock
Shared Data
std::mutex m;
int x = 0;
int y = 0;
Execution
Thread 1 Execution Thread 2 Execution
m.lock();
A x = 1; m.lock();
B y = x;
m.unlock();
since unlock synchronizes with lock, A happens before B; thus, for timing shown, B must see 1 for x
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1051
Memory Orders
Most operations on atomic types allow a memory order to be specified.
Example:
std::atomic<int> x = 0;
x.store(42, std::memory_order_seq_cst);
int y = x.load(std::memory_order_seq_cst);
The following memory orders are supported:
sequentially consistent (std::memory_order_seq_cst)
acquire-release (std::memory_order_acq_rel)
acquire (std::memory_order_acquire)
release (std::memory_order_release)
consume (std::memory_order_consume)
relaxed (std::memory_order_relaxed)
simultaneously (i.e., total order for all writes to all atomic objects); and
whether operations on atomic objects in different threads can establish a
synchronization relationship (namely, a synchronizes-with or
dependency-ordered-before [discussed later] relationship).
The models listed from strongest (i.e., makes the most guarantees) to
weakest (i.e., makes the least guarantees) are:
1 sequentially consistent,
2 acquire release,
3 consume release, and
4 relaxed.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1053
Memory Models (Continued 1)
These models are hierarchical in the sense that each model makes at
least all of the same guarantees as its weaker counterparts.
As we proceed from stronger to weaker models, more guarantees are lost.
A stronger model may require additional synchronization by hardware,
which can degrade performance.
A weaker model may not provide sufficient guarantees for the correct
functioning of code.
Using a model that fails to provide sufficient guarantees for correct code
behavior will result in bugs.
Also, as the model is weakened, it becomes more difficult to reason about
the behavior of code, leading to incomprehensible code and an increased
likelihood of (often very subtle) bugs.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1054
Modification Order
All writes to a particular atomic object M (over its lifetime) occur in some
particular total order, called its modification order.
Each atomic object has its own well-defined modification order.
For a particular atomic object M , all threads in a program are guaranteed
to see M change in a manner consistent with its modification order.
Essentially, this guarantee ensures that, once a given thread has seen a
particular value of an atomic object, a subsequent read by that thread
cannot retrieve an earlier value of the object.
If such a guarantee were not made, the memory model would be so weak
as to be impractical to use.
Modification order is primarily a conceptual tool that is useful for
describing memory-model behavior.
In practice, a thread is unlikely to actually observe every change in the
modification order of an object.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1055
Modification Order (Continued)
For each atomic object M , each thread has its own current position in
object’s modification order.
A thread’s current position in the modification order of a particular atomic
object need not be the same for all threads.
A read from an atomic object M by a thread T can optionally move T ’s
current position to a later position in the modification order of M and then
returns the value at the current position.
A write to an atomic object M by a thread T appends the value to be
written to the modification order of M and updates T ’s current position in
the modification order of M to correspond to the value written.
An read-modify-write operation A on an atomic object M reads the last
value in the modification order of M , modifies the value read appropriately,
appends the resulting value to the modification order of M , and updates
T ’s current position in the modification order of M to correspond to the
value written.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1056
Modification Order Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1057
Relative Ordering of Changes to Different Atomic Objects
Although each atomic object has its own well-defined modification order, it
is not necessarily the case that the modification orders for individual
objects can be combined into a single total order over all atomic objects.
Practically speaking, the reason for this is the delay in the visibility of
results introduced by store buffers, caches, and so on.
If a single total order for writes to all atomic objects is not guaranteed, this
implies that the relative order of changes to different atomic objects need
not appear the same to different threads.
Ensuring the existence of a single total order over all atomic objects would
require a significant amount of additional processor synchronization,
which can significantly degrade performance.
Therefore, this guarantee is not required to be made in all cases, the idea
being that we only ask for the guarantee when it is needed for correct
code behavior.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1058
Modification Order Revisited
Consider a program with two threads and two shared integer atomic
objects x and y, each having the modification order: 0, 1.
Suppose that no requirement is imposed to guarantee the existence of a
single total order on writes to all atomic objects.
Thread 1 could see x and y change in the following manner, consistent
with their stated modification order:
Variable Updates to Value Seen By Thread
x 0 ........ 1 .........................
y 0 ......................... 1 ........
Observe that thread 1 and thread 2 do not see x and y change in the
same order relative to one another (i.e., thread 1 sees x change before y,
while thread 2 sees y change before x).
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1059
Sequentially-Consistent Model
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1060
Example: Sequentially-Consistent Model
shared data:
x and y are of type std::atomic<int> and both are initially zero
thread 1 code (writes x):
x.store(1, std::memory_order_seq_cst);
thread 2 code (writes y):
y.store(1, std::memory_order_seq_cst);
thread 3 code (reads x then y):
int x1 = x.load(std::memory_order_seq_cst);
int y1 = y.load(std::memory_order_seq_cst);
thread 4 code (reads y then x):
int y2 = y.load(std::memory_order_seq_cst);
int x2 = x.load(std::memory_order_seq_cst);
memory order guarantees total order for all writes to all atomic objects
so, thread 3 and thread 4 must agree about order in which x and y are
modified
not possible to see x1 == 1 and y1 == 0 in thread 3 (implying x
modified before y) and x2 == 0 and y2 == 1 in thread 4 (implying y
modified before x)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1061
Example: Sequentially-Consistent Model
1 #include <atomic>
2 #include <thread>
3 #include <cassert>
4
5 std::atomic<int> x, y, c;
6
7 void w_x() {x.store(1, std::memory_order_seq_cst);}
8
9 void w_y() {y.store(1, std::memory_order_seq_cst);}
10
11 void r_xy() {
12 while (!x.load(std::memory_order_seq_cst)) {}
13 if (y.load(std::memory_order_seq_cst)) {++c;}
14 }
15
16 void r_yx() {
17 while (!y.load(std::memory_order_seq_cst)) {}
18 if (x.load(std::memory_order_seq_cst)) {++c;}
19 }
20
21 int main() {
22 x = 0; y = 0; c = 0;
23 std::thread t1(w_x), t2(w_y), t3(r_xy), t4(r_yx);
24 t1.join(); t2.join(); t3.join(); t4.join();
25 assert(c != 0); // assertion cannot fail
26 }
assertion cannot fail: when while loop in r_xy terminates, all threads must see x as nonzero; when while loop in r_yx terminates,
all threads must see y as nonzero; at least one of these must happen before if statements in both r_xy and r_yx executed
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1062
Acquire-Release Model
For the acquire-release model, the memory order is chosen as follows:
a read operation uses the acquire order (std::memory_order_acquire)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1069
Inter-Thread Happens-Before Relationships Revisited
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1070
Consume-Release Model
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1071
Example: Consume-Release Model
1 #include <thread>
2 #include <atomic>
3 #include <cassert>
4
5 int x = 0;
6 std::atomic y(0);
7
8 void producer() {
9 x = 42;
10 y.store(1, std::memory_order_release);
11 }
12
13 void consumer() {
14 int a;
15 while (!(a = y.load(std::memory_order_consume))) {}
16 assert(x == 42); // data race
17 }
18
19 int main() {
20 std::thread t1(producer);
21 std::thread t2(consumer);
22 t1.join();
23 t2.join();
24 }
program has data race on x; a does not carry dependency to x so x = 42
does not necessarily happen before x used in assertion
if consume changed to acquire, no data race and assertion cannot fail
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1072
Example: Publishing Data Via Pointer
1 #include <thread>
2 #include <atomic>
3 #include <cassert>
4 #include <string>
5
6 std::atomic<std::string*> p(nullptr);
7 int x = 0;
8
9 void producer() {
10 std::string* s = new std::string("Hello");
11 x = 42;
12 p.store(s, std::memory_order_release);
13 }
14
15 void consumer() {
16 std::string* s;
17 while (!(s = p.load(std::memory_order_consume))) {}
18 assert(*s == "Hello");
19 // assert(x == 42); would result in data race
20 }
21
22 int main() {
23 std::thread t1(producer), t2(consumer);
24 t1.join(); t2.join();
25 }
assertion cannot fail; store to p is dependency ordered before load and
load carries dependency to *s in assertion
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1073
Relaxed Model
For the relaxed model, all memory operations use the relaxed order
(std::memory_order_relaxed).
Like in the acquire-release model, no total order exists on updates to all
atomic objects (collectively).
Operations on the same variable within a single thread satisfy a
happens-before relationship (i.e., within a single thread, accesses to a
single atomic variable must follow program order).
Unlike in the acquire-release model, no inter-thread synchronization
relationship is established.
No requirement exists on the ordering relative to other threads.
The relaxed order is sometime suitable for updating counters (e.g., blind
event counters).
Except in very trivial cases, it can be extremely difficult to reason about
the meaning and/or correctness of code that uses relaxed order.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1074
Behavior of Relaxed Model
consider atomic memory operations with relaxed order
for each individual atomic object, all threads have view of updates that is
consistent with single modification sequence
read operation (e.g., load):
if current position not set, return any element in sequence and set current
position to that of returned element
otherwise, either leave current position unchanged or move later in
sequence and return value at current position
write operation (e.g., store):
append value to end of sequence
set current position to correspond to appended value
read-modify-write operation (e.g., increment, decrement, exchange,
compare_exchange):
read last value from sequence
modify read value as appropriate to obtain new value
append new value to end of sequence
set current position to correspond to that of appended value
considerable flexibility in value returned by read
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1075
Example: Relaxed Model
1 #include <atomic>
2 #include <thread>
3 #include <cassert>
4
5 std::atomic<int> x, y, c;
6
7 void w_x() {x.store(1, std::memory_order_relaxed);}
8
9 void w_y() {y.store(1, std::memory_order_relaxed);}
10
11 void r_xy() {
12 while (!x.load(std::memory_order_relaxed)) {}
13 if (y.load(std::memory_order_relaxed)) {++c;}
14 }
15
16 void r_yx() {
17 while (!y.load(std::memory_order_relaxed)) {}
18 if (x.load(std::memory_order_relaxed)) {++c;}
19 }
20
21 int main() {
22 x = 0; y = 0; c = 0;
23 std::thread t1(w_x), t2(w_y), t3(r_xy), t4(r_yx);
24 t1.join(); t2.join(); t3.join(); t4.join();
25 assert(c != 0); // assertion can fail
26 }
assertion can fail: one thread seeing x or y being nonzero does not imply
other thread sees same
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1076
Example: Blind Event Counters
1 #include <vector>
2 #include <iostream>
3 #include <thread>
4 #include <atomic>
5
6 std::atomic<unsigned long long> counter(0);
7
8 void doWork() {
9 for (long i = 0; i < 100’000L; ++i) {
10 counter.fetch_add(1, std::memory_order_relaxed);
11 }
12 }
13
14 int main() {
15 std::vector<std::thread> workers;
16 for (int i = 0; i < 10; ++i) {
17 workers.emplace_back(doWork);
18 }
19 for (auto& t : workers) {
20 t.join();
21 }
22 std::cout << "counter " << counter << ’\n’;
23 }
fetch_add can use relaxed order, since only incrementing counter blindly (i.e., not
taking action based on value of counter)
thread join operations provide synchronization to ensure desired value read for counter
when output
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1077
Example: Done Flag
1 #include <vector>
2 #include <thread>
3 #include <atomic>
4 #include <chrono>
5
6 std::atomic<bool> done;
7
8 void doWork() {
9 while (!done.load(std::memory_order_relaxed)) {
10 // do something here
11 }
12 }
13
14 int main() {
15 std::vector<std::thread> workers;
16 done.store(false, std::memory_order_relaxed); // I hope? ;)
17 for (int i = 0; i < 16; ++i) {
18 workers.emplace_back(doWork);
19 }
20 std::this_thread::sleep_for(std::chrono::seconds(5));
21 done = true; // not relaxed
22 for (auto& t : workers) {
23 t.join();
24 }
25 }
done.store can be relaxed due to synchronization from thread create
done.load can be relaxed since order not important; different order as if other threads ran at different speeds
assign to done must be sequentially-consistent to prevent assign from floating past join (due to single-thread optimization)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1078
Example: std::shared_ptr Reference Counting
The copy constructor for shared_ptr (which increments a reference
count) would look something like:
// ...
controlBlockPtr = other->controlBlockPtr;
controlBlockPtr->refCount.fetch_add(1,
std::memory_order_relaxed);
// ...
The destructor for shared_ptr (which decrements a reference count)
would look something like:
// ...
if (!controlBlockPtr->refCount.fetch_sub(1,
std::memory_order_acq_rel)) {
delete controlBlockPtr;
}
// ...
The increment operation can use relaxed order, since no action is taken
based on the reference count value.
The decrement operation needs to use acquire-release order so that the
decrement cannot float and the correct view of the data is seen by the
thread doing the delete (all decrements form a synchronization chain).
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1079
Release Semantics for Memory Operations
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1080
Acquire Semantics for Memory Operations
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1081
Release Sequences
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1082
Release Sequence Example
1 #include <thread>
2 #include <atomic>
3 #include <cassert>
4
5 int x = 0;
6 std::atomic y(0);
7
8 int main() {
9 std::thread t1([](){
10 x = 42;
11 y.store(1, std::memory_order_release); // A
12 y.store(2, std::memory_order_relaxed); // B
13 });
14 std::thread t2([](){
15 int r;
16 while ((r = y.load(std::memory_order_acquire)) // C
17 < 2) {}
18 assert(x == 42);
19 });
20 t1.join();
21 t2.join();
22 }
stores to y in A and B constitute release sequence headed by store in A
when while loop terminates, load in C will have read value written by store in B (not store in A)
A synchronizes with C, since C reads value in release sequence headed by A
assertion cannot fail, since A happens before C
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1083
Fences
A memory fence (also known as a memory barrier) is an operation that
causes the processor and compiler to enforce an ordering constraint on
memory operations issued before and after the fence operation.
Certain types of memory operations before a fence are guaranteed not to
be reordered with certain types of memory operations after the fence.
A fence may also introduce synchronizes-with relationships under certain
circumstances.
An acquire fence prevents the reordering of any read or write following
the fence (in program order) with any read prior to the fence (in program
order). (That is, a memory operation after the fence cannot be moved
before any read operation before the fence.)
A release fence prevents the reordering of any read or write prior to the
fence (in program order) with any write following the fence (in program
order). (That is, a memory operation before the fence cannot be moved
after any write operation after the fence.)
A fence is not a release or acquire operation. as it does not read/write
memory.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1084
std::atomic_thread_fence
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1085
Fences and Synchronizes-With Relationships
Release fence and acquire fence. A release fence A synchronizes with an
acquire fence B if there exist atomic operations X and Y , both operating
on some atomic object M , such that A is sequenced before X , X modifies
M , Y is sequenced before B, and Y reads the value written by X or a
value written by any side effect in the hypothetical release sequence X
would head if it were a release operation.
Release fence and acquire operation. A release fence A synchronizes
with an atomic operation B that performs an acquire operation on an
atomic object M if there exists an atomic operation X such that A is
sequenced before X , X modifies M , and B reads the value written by X or
a value written by any side effect in the hypothetical release sequence X
would head if it were a release operation.
Release operation and acquire fence. An atomic operation A that is a
release operation on an atomic object M synchronizes with an acquire
fence B if there exists some atomic operation X on M such that X is
sequenced before B and reads the value written by A or a value written by
any side effect in the release sequence headed by A.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1086
Example: Incorrect Code Without Fence
1 #include <thread>
2 #include <atomic>
3 #include <iostream>
4
5 std::atomic ready(false);
6 int data = 0;
7
8 void produce() {
9 data = 42; // write to data can move after store in A
10 // release fence needed here
11 ready.store(true, std::memory_order_relaxed); // A
12 }
13
14 void consume() {
15 while (!ready.load(std::memory_order_relaxed)) {} // B
16 // acquire fence needed here
17 std::cout << data << ’\n’;
18 // read of data can move before load in B
19 }
20
21 int main() {
22 std::thread t1(produce);
23 std::thread t2(consume);
24 t1.join(); t2.join();
25 }
atomic store (to ready) does not synchronize with atomic load (of ready),
due to relaxed order; results in race on data
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1087
Example: Correct Code With Fence
1 #include <thread>
2 #include <atomic>
3 #include <iostream>
4
5 std::atomic ready(false);
6 int data = 0;
7
8 void produce() {
9 data = 42;
10 std::atomic_thread_fence(std::memory_order_release);
11 ready.store(true, std::memory_order_relaxed);
12 }
13
14 void consume() {
15 while (!ready.load(std::memory_order_relaxed)) {}
16 std::atomic_thread_fence(std::memory_order_acquire);
17 std::cout << data << ’\n’;
18 }
19
20 int main() {
21 std::thread t1(produce);
22 std::thread t2(consume);
23 t1.join(); t2.join();
24 }
release fence synchronizes with acquire fence, due to atomic load (of
ready) reading from result of atomic store (to ready)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1088
Memory Orders: The Bottom Line
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1089
Section 3.5.11
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1090
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1091
References II
4 M. Herlihy and N. Shavit. The Art of Multiprocessor Programming.
Morgan Kaufmann, Burlington, MA, USA, 2008.
A good reference for concurrent programming.
7 H.-J. Boehm and S. V. Adve. You don’t know jack about shared variables
or memory models.
Communications of the ACM, 55(2):48–54, Feb. 2012.
8 H.-J. Boehm, Memory Model Rationale, ISO/IEC
JTC1/SC22/WG14/N1479, May 2010. http://www.open-std.org/
jtc1/sc22/wg14/www/docs/n1479.htm
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1092
Talks I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1093
Talks II
4 Hans-J. Boehm. Threads and Shared Variables in C++11. Going Native,
Redmond, WA, USA, Feb. 2–3, 2012. Available online at https://
channel9.msdn.com/Events/GoingNative/GoingNative-2012/
Threads-and-Shared-Variables-in-C-11.
5 Mike Long. Introducing the C++ Memory Model. Norwegian Developers
Conference, Oslo, Norway, Jun. 15–19, 2014. Available online at
https://vimeo.com/97419179.
6 Herb Sutter. Machine Architecture and You: Things Your Programming
Language Never Told You. Northwest C++ Users’ Group, Redmond, WA,
USA, Sept. 19. 2007. Available online at http://nwcpp.org/
september-2007.html.
7 Pablo Halpern. Overview of Parallel Programming in C++, CppCon,
Bellevue, WA, USA, Sept. 8, 2014. Available online at https://youtu.
be/y0GSc5fKtl8.
8 Valentin Ziegler. C++ Memory Model, Meeting C++, Berlin, Germany,
Dec. 6, 2014. Available online at https://youtu.be/gpsz8sc6mNU.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1094
Talks III
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1095
Part 4
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1096
Section 4.1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1097
Undefined, Unspecified, and Implementation-Defined
Behavior
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1101
Examples of Implementation-Defined Behavior
meaning of #pragma directive
nesting limit for #include directives
search locations for "" and <> headers
sequence of places searched for header
signedness of char
sizeof built-in types other than char, signed char,
unsigned char
type of size_t, ptrdiff_t
parameters to main function
alignment (i.e., restrictions on the addresses at which an object of a
particular type can be placed)
result of right shift of negative value
precise types used in various parts of C++ standard library (e.g., actual
type named by vector<T>::iterator )
meaning of asm declaration
for more examples, see “Index of implementation-defined behavior”
section in C++11 standard
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1102
Private Member Access Without Friends (Legal But Evil)
1 #include <iostream>
2
3 template <typename Tag>
4 typename Tag::type saved_private_v;
5
6 template <typename Tag, typename Tag::type x>
7 bool save_private_v = (saved_private_v<Tag> = x);
8
9 class Widget {
10 public:
11 Widget(int i) : i_(i) {}
12 private:
13 int i_;
14 int f_() const {return i_;}
15 };
16
17 struct Widget_i_ {using type = int Widget::*;};
18 struct Widget_f_ {using type = int (Widget::*)() const;};
19
20 template bool save_private_v<Widget_i_, &Widget::i_>;
21 template bool save_private_v<Widget_f_, &Widget::f_>;
22
23 int main() {
24 Widget w(42);
25 std::cout << w.*saved_private_v<Widget_i_> << ’\n’;
26 std::cout << (w.*saved_private_v<Widget_f_>)() << ’\n’;
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1103
Section 4.2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1104
Use of std::istream::eof
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1107
Use of std::endl
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1108
Use of std::endl (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1109
Stream Extraction Failure
for built-in types, if stream extraction fails, value of target for stream
extraction depends on reason for failure
in following example, what is value of x if stream extraction fails:
int x;
std::cin >> x;
if (!std::cin) {
// what is value of x?
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1111
The abs Function
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1113
Types of Literals
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1114
Testing Failure State of Streams
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1115
Member Initialization Order
1 #include <vector>
2 std::vector<int> v = {1, 2, 3, 4};
1 #include <vector>
2 extern std::vector<int> v;
3 std::vector<int> w = {v[0], v[1]};
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1117
Implement Postfix Increment/Decrement via Prefix
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1118
Sizeof Class Versus Sum of Member Sizes
compilers can (and do) add padding to classes/structs
Example:
1 #include <iostream>
2
3 class Widget {
4 // ...
5 private:
6 char c;
7 int i;
8 };
9
10 int main() {
11 // two numbers printed not necessarily the same
12 std::cout << sizeof(char) + sizeof(int) << ’ ’ <<
13 sizeof(Widget) << ’\n’;
14 std::cout << alignof(int) << ’ ’ <<
15 alignof(Widget) << ’\n’;
16 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1120
Division/Modulus Operator and Negative Numbers
for integral operands, division operator yields algebraic quotient with any
fractional part discarded (i.e., round towards zero)
if quotient a / b is representable in type of result,
(a / b) * b + a % b is equal to a
so, assuming b is not zero and no overflow, a % b equals
a - (a / b) * b
result of modulus operator not necessarily nonnegative
Example:
1 #include <cassert>
2
3 int main() {
4 assert(5 % 3 == 2);
5 assert(5 % (-3) == 2);
6 assert((-5) % 3 == -2);
7 assert((-5) % (-3) == -2);
8 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1121
std::string Concatenation
Unnecessary temporaries!
Fix:
func(s + ", " + p);
func(p + ", "s + s);
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1122
std::vector<std::string> Insertion
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1123
Classes Holding Multiple Resources
What is wrong with this code?
class TwoResources {
public:
TwoResources() : x_(nullptr) : y_(nullptr) {
x_ = new X;
y_ = new Y;
}
˜TwoResources() {
delete x_;
delete y_;
}
private:
X* x_;
Y* y_;
};
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1124
Avoid Returning By Const Value
The const return value will interact poorly with move semantics, as the
returned object cannot be used as the source for a move operation (since
the source for a move operation must be modifiable).
Fix:
std::string getMessage() {
return "Hello";
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1125
Normally Avoid Using std::move When Returning By Value
Due to the use of std::move, the type of the expression in the return
statement does not match the function return type (i.e.,
std::vector<int> versus std::vector<int>&&).
RVO/NRVO can only be applied if the type of the expression in the return
statement matches the function return type.
So, RVO/NRVO cannot be applied in this case.
If the types would not have matched anyways (e.g., a two-element
std::tuple and a std::pair), std::move would be reasonable to
employ.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1126
Avoid Returning an Rvalue Reference to an Rvalue Reference Parameter
Returning an rvalue reference to an rvalue reference parameter can
potentially lead to very subtle bugs.
Example:
std::string&& join(std::string&& s, const char* p) {
return std::move(s.append(", ").append(p));
}
std::string getMessage() {return "Hello";}
void func() {
const string& r = join(getMessage(), " World");
// lifetime of temporary returned by getMessage
// not extended to lifetime of r since not
// directly bound to r
// r now refers to destroyed temporary
}
Fix:
std::string join(std::string&& s, const char* p) {
return std::move(s.append(", ").append(p));
}
Returning by rvalue reference should probably be avoided, except in very
special circumstances (such as std::forward and std::move).
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1127
No Explicit Template Arguments to std::make_pair
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1128
Prefer Use of std::make_shared
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1129
Be Careful When Mixing Signed and Unsigned Types
1 #include <cassert>
2
3 int main() {
4 short ss = -1;
5 int si = -1;
6 long sl = -1;
7 long long sll = -1;
8 unsigned short us = 0;
9 unsigned int ui = 0;
10 unsigned long ul = 0;
11 unsigned long long ull = 0;
12 // comparison between signed and unsigned types
13 assert(ss < ui); // FAILS: ss becomes UINT_MAX
14 // comparison between signed and unsigned types
15 assert(si < ui); // FAILS: si becomes UINT_MAX
16 // comparison between signed and unsigned types
17 assert(sl < ul); // FAILS: sl becomes ULONG_MAX
18 // comparison between signed and unsigned types
19 assert(sll < ull); // FAILS: sll becomes ULONGLONG_MAX
20 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1130
Section 4.3
Idioms
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1131
Proxy Classes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1132
Proxy Class Example
1 #include <iostream>
2 #include <utility>
3
4 class BoolVector;
5
6 class Proxy {
7 public:
8 ˜Proxy() = default;
9 Proxy& operator=(const Proxy&) = default;
10 operator bool() const;
11 void operator=(bool b);
12 private:
13 friend class BoolVector;
14 Proxy(const Proxy&) = default;
15 Proxy(BoolVector* v, int i) : v_(v), i_(i) {}
16 BoolVector* v_;
17 int i_;
18 };
19
20 class BoolVector {
21 public:
22 BoolVector(int n) : n_(n), d_(new unsigned char[(n + 7) / 8]) {
23 std::fill_n(d_, (n + 7) / 8, 0);
24 }
25 ˜BoolVector() {delete [] d_;}
26 int size() const {return n_;}
27 bool operator[](int i) const {return getElem(i);}
28 Proxy operator[](int i) {return Proxy(this, i);}
29 private:
30 friend class Proxy;
31 bool getElem(int i) const {return (d_[i / 8] >> (i % 8)) & 1;}
32 void setElem(int i, bool b) {
33 (d_[i / 8] &= ˜(1 << (i % 8))) |= (b << (i % 8));
34 }
35 int n_;
36 unsigned char* d_;
37 };
38
39 inline void Proxy::operator=(bool b) {v_->setElem(i_, b);}
40 inline Proxy::operator bool() const {return v_->getElem(i_);}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1133
Proxy Class Example (Continued)
1 #include "proxy_class_example_1.hpp"
2
3 int main() {
4 BoolVector v(16);
5 for (int i = 0; i < v.size(); ++i) {
6 v[i] = (i & 1);
7 }
8 for (int i = 0; i < v.size(); ++i) {
9 std::cout << v[i];
10 }
11 std::cout << ’\n’;
12 const BoolVector& cv = v;
13 for (int i = 0; i < cv.size(); ++i) {
14 std::cout << cv[i];
15 }
16 std::cout << ’\n’;
17 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1134
Section 4.4
C++ Compatibility
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1135
C++ Compatibility
many changes have been made to C++ language and standard library
during evolution of C++ from C++98 to present
some changes resulted in incompatibilties between different versions of
C++ standard
subsequent slides list some reference material that discusses how C++
standard changed from one version to next
knowing such changes helps to understand incompatibilities between
different versions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1136
Talks I
C Compatibility
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1138
C Compatibility
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1139
Conflicts with New Keywords
1 #include <stdio.h>
2 #include <unistd.h>
3
4 /* Delete a file. */
5 int delete(const char* filename) { /* note function name */
6 return unlink(filename);
7 }
8
9 int main(int argc, char** argv) {
10 if (argc >= 2) {
11 if (delete(argv[1])) {
12 printf("cannot delete file\n");
13 return 1;
14 }
15 }
16 return 0;
17 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1140
Function Declarations Without Arguments
1 #include <stdio.h>
2
3 int plusOne(); /* no arguments specified */
4
5 int main(int argc, char** argv) {
6 printf("%d\n", plusOne(0));
7 return 0;
8 }
9
10 int plusOne(int i) {
11 return i + 1;
12 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1141
Implicit Return Type
1 #include <stdio.h>
2
3 myfunc() { /* implicit return type */
4 return 3;
5 }
6
7 int main(int argc, char **argv) {
8 int i;
9 i = myfunc();
10 printf("%d\n", i);
11 return 0;
12 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1142
More Restrictive Conversions Involving void*
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1143
Scoping Rules for Nested Structs
1 struct outer {
2 struct inner {
3 int i;
4 };
5 int j;
6 };
7
8 struct inner a = {1}; /* inner vs. outer::inner */
9
10 int main(int argc, char** argv) {
11 return 0;
12 }
C and C++ both allow nested struct types, but the scoping rules differ.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1144
Part 5
Libraries
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1145
Section 5.1
Boost Libraries
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1146
Section 5.1.1
Introduction
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1147
Boost Libraries
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1148
Some Boost Libraries
Iterators
Library Description
Iterator concepts that extend C++ standard iterator requirements
and components for building iterators based on these con-
cepts; includes several iterator adaptors
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1149
Some Boost Libraries (Continued 1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1150
Some Boost Libraries (Continued 2)
Input/Output
Library Description
I/O State Savers classes for saving/restoring state associated with
I/O streams
Miscellaneous
Library Description
Program Options process program options via command line or con-
figuration file
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1151
Some Boost Libraries (Continued 3)
Concurrent Programming
Library Description
Fiber userland threads library
Compute parallel/GPU computing library
Lockfree lockfree containers (e.g., stacks and queues)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1152
Section 5.1.2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1153
Boost Container Library
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1154
Container Types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1155
Container Types (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1156
Section 5.1.3
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1157
Boost Intrusive Library
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1158
Container Types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1159
Container Types (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1160
Base and Member Hooks
hook is class object that must be added to a user’s class in order for
user’s class to be usable with intrusive container
hook used to encapsulate data used to manage nodes in container (e.g.,
successor and predecessor links for doubly-linked list)
two kinds of hooks:
1 base hook
2 member hook
base hook is included in user’s class as base class object using public
inheritance
member hook included in user’s class as public data member
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1161
slist With Base Hook
1 #include <iostream>
2 #include <vector>
3 #include <boost/intrusive/slist.hpp>
4
5 namespace bi = boost::intrusive;
6
7 struct Widget : public bi::slist_base_hook<> {
8 Widget(int i_) : i(i_) {}
9 int i;
10 };
11
12 using WidgetList = bi::slist<Widget>;
13
14 int main() {
15 std::vector<Widget> buffer;
16 for (int i = 0; i < 10; ++i) {buffer.push_back(Widget(i));}
17 WidgetList a;
18 for (auto&& i : buffer) {a.push_front(i);}
19 for (auto i = a.begin(); i != a.end(); ++i) {
20 if (i != a.begin()) {std::cout << ’ ’;}
21 std::cout << i->i;
22 }
23 std::cout << ’\n’;
24 while (!a.empty()) {a.erase_after(a.before_begin());}
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1162
slist With Member Hook
1 #include <iostream>
2 #include <vector>
3 #include <boost/intrusive/slist.hpp>
4
5 namespace bi = boost::intrusive;
6
7 struct Widget {
8 Widget(int i_) : i(i_) {}
9 int i;
10 bi::slist_member_hook<> hook;
11 };
12
13 using WidgetList = bi::slist<Widget, bi::member_hook<Widget,
14 bi::slist_member_hook<>, &Widget::hook>>;
15
16 int main() {
17 std::vector<Widget> buffer;
18 for (int i = 0; i < 10; ++i) {buffer.push_back(Widget(i));}
19 WidgetList a;
20 for (auto&& i : buffer) {a.push_front(i);}
21 for (auto i = a.begin(); i != a.end(); ++i) {
22 if (i != a.begin()) {std::cout << ’ ’;}
23 std::cout << i->i;
24 }
25 std::cout << ’\n’;
26 while (!a.empty())
27 {a.erase_after(a.before_begin());}
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1163
list With Base Hook
1 #include <iostream>
2 #include <vector>
3 #include <boost/intrusive/list.hpp>
4
5 namespace bi = boost::intrusive;
6
7 struct Widget : public bi::list_base_hook<> {
8 Widget(int i_) : i(i_) {}
9 int i;
10 };
11
12 using WidgetList = bi::list<Widget>;
13
14 int main() {
15 std::vector<Widget> buffer;
16 for (int i = 0; i < 10; ++i) {buffer.push_back(Widget(i));}
17 WidgetList a;
18 for (auto&& i : buffer) {a.push_back(i);}
19 for (auto i = a.begin(); i != a.end(); ++i) {
20 if (i != a.begin()) {std::cout << ’ ’;}
21 std::cout << i->i;
22 }
23 std::cout << ’\n’;
24 while (!a.empty()) {a.erase(a.begin());}
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1164
list With Member Hook
1 #include <iostream>
2 #include <vector>
3 #include <boost/intrusive/list.hpp>
4
5 namespace bi = boost::intrusive;
6
7 struct Widget {
8 Widget(int i_) : i(i_) {}
9 int i;
10 bi::list_member_hook<> hook;
11 };
12
13 using WidgetList = bi::list<Widget, bi::member_hook<Widget,
14 bi::list_member_hook<>, &Widget::hook>>;
15
16 int main() {
17 std::vector<Widget> buffer;
18 for (int i = 0; i < 10; ++i) {buffer.push_back(Widget(i));}
19 WidgetList a;
20 for (auto&& i : buffer) {a.push_back(i);}
21 for (auto i = a.begin(); i != a.end(); ++i) {
22 if (i != a.begin()) {std::cout << ’ ’;}
23 std::cout << i->i;
24 }
25 std::cout << ’\n’;
26 while (!a.empty()) {a.erase(a.begin());}
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1165
list With Multiple Base Hooks
1 #include <iostream>
2 #include <vector>
3 #include <boost/intrusive/list.hpp>
4
5 namespace bi = boost::intrusive;
6
7 struct Alpha {};
8 struct Beta {};
9 struct Widget : public bi::list_base_hook<bi::tag<Alpha>>,
10 public bi::list_base_hook<bi::tag<Beta>> {
11 Widget(int i_) : i(i_) {}
12 int i;
13 };
14
15 int main() {
16 std::vector<Widget> buffer;
17 for (int i = 0; i < 10; ++i) {buffer.push_back(Widget(i));}
18 bi::list<Widget, bi::base_hook<bi::list_base_hook<bi::tag<Alpha>>>>
19 a;
20 bi::list<Widget, bi::base_hook<bi::list_base_hook<bi::tag<Beta>>>>
21 b;
22 for (auto&& i : buffer)
23 {a.push_back(i); b.push_front(i);}
24 for (auto&& w : a) {std::cout << w.i << ’\n’;}
25 std::cout << ’\n’;
26 for (auto&& w : b) {std::cout << w.i << ’\n’;}
27 while (!a.empty()) {a.erase(a.begin());}
28 while (!b.empty()) {b.erase(b.begin());}
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1166
set With Base Hook
1 #include <iostream>
2 #include <vector>
3 #include <boost/intrusive/set.hpp>
4
5 namespace bi = boost::intrusive;
6
7 struct Widget : public bi::set_base_hook<> {
8 Widget(int i_) : i(i_) {}
9 bool operator<(const Widget& other) const {return i < other.i;}
10 int i;
11 };
12
13 int main() {
14 int values[] = {1, 3, 5, 7, 9, 0, 2, 4, 6, 8};
15 std::vector<Widget> buffer;
16 for (auto i : values) {buffer.push_back(Widget(i));}
17 bi::set<Widget> a;
18 for (auto&& i : buffer) {a.insert(a.end(), i);}
19 for (auto&& w : a) {std::cout << w.i << ’\n’;}
20 if (a.find(7) != a.end()) {std::cout << "7 is in set\n";}
21 while (!a.empty())
22 {a.erase(a.begin());}
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1167
set and list With Base Hooks
1 #include <iostream>
2 #include <vector>
3 #include <boost/intrusive/set.hpp>
4 #include <boost/intrusive/list.hpp>
5
6 namespace bi = boost::intrusive;
7
8 struct Widget : public bi::set_base_hook<>,
9 public bi::list_base_hook<> {
10 Widget(int i_) : i(i_) {}
11 bool operator<(const Widget& other) const {return i < other.i;}
12 int i;
13 };
14
15 int main() {
16 int values[] = {1, 3, 5, 7, 9, 0, 2, 4, 6, 8};
17 std::vector<Widget> buffer;
18 for (auto i : values) {buffer.push_back(Widget(i));}
19 bi::set<Widget> a;
20 bi::list<Widget> b;
21 for (auto&& i : buffer)
22 {a.insert(a.end(), i); b.push_back(i);}
23 if (a.find(7) != a.end()) {std::cout << "found 7\n\n";}
24 for (auto&& w : a) {std::cout << w.i << ’\n’;}
25 std::cout << ’\n’;
26 for (auto&& w : b) {std::cout << w.i << ’\n’;}
27 while (!a.empty()) {a.erase(a.begin());}
28 while (!b.empty()) {b.erase(b.begin());}
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1168
Achieving Map Functionality With set
1 #include <iostream>
2 #include <vector>
3 #include <boost/intrusive/set.hpp>
4
5 namespace bi = boost::intrusive;
6
7 struct Widget : public bi::set_base_hook<> {
8 Widget(int i_, int j_) : i(i_), j(j_) {}
9 int i;
10 int j;
11 };
12
13 struct Is_key {
14 using type = int;
15 const type& operator()(const Widget& w) const
16 {return w.i;}
17 };
18
19 int main() {
20 int values[] = {1, 3, 5, 7, 9, 0, 2, 4, 6, 8};
21 std::vector<Widget> buffer;
22 for (auto i : values) {buffer.push_back(Widget(i, -i));}
23 bi::set<Widget, bi::key_of_value<Is_key>> a;
24 for (auto&& i : buffer) {a.insert(a.end(), i);}
25 for (auto&& w : a) {std::cout << w.i << ’ ’ << w.j << ’\n’;}
26 auto i = a.find(5);
27 std::cout << i->j << ’\n’;
28 while (!a.empty()) {a.erase(a.begin());}
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1169
unordered_set With Base Hook
1 #include <iostream>
2 #include <vector>
3 #include <boost/intrusive/unordered_set.hpp>
4
5 namespace bi = boost::intrusive;
6
7 class Widget : public bi::unordered_set_base_hook<> {
8 public:
9 Widget(int value = 0) : value_(value) {}
10 int get_value() const {return value_;}
11 private:
12 int value_;
13 };
14
15 bool operator==(const Widget& a, const Widget& b)
16 {return a.get_value() == b.get_value();}
17
18 std::size_t hash_value(const Widget& a) {return std::size_t(a.get_value());}
19
20 int main() {
21 std::vector<Widget> widgets;
22 for (int i = 0; i < 10; ++i) {widgets.push_back(Widget(i));}
23 using bucket_type = bi::unordered_set<Widget>::bucket_type;
24 using bucket_traits = bi::unordered_set<Widget>::bucket_traits;
25 bucket_type buckets[100];
26 bi::unordered_set<Widget> s(bucket_traits(buckets, 100));
27 for (auto&& w : widgets) {s.insert(w);}
28 for (auto&& i : s) {std::cout << i.get_value() << ’\n’;}
29 if (s.find(7) != s.end()) {std::cout << "found 7\n";}
30 return 0;
31 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1170
Section 5.1.4
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1171
Boost Iterator Library
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1172
Forward Iterator Example: Iterator Class Without Boost (1)
1 #include <type_traits>
2 #include <iterator>
3
4 // singly-linked list node base (for intrusive container)
5 template <class T> struct slist_node_base {
6 slist_node_base(T* next_) : next(next_) {}
7 T* next; // pointer to next node in list
8 };
9
10 // single-linked list iterator (const and non-const)
11 template <class T> class slist_iter {
12 public:
13 using iterator_category = std::forward_iterator_tag;
14 using value_type = typename std::remove_const_t<T>;
15 using reference = T&;
16 using pointer = T*;
17 slist_iter(T* node = nullptr) : node_(node) {}
18 template <class OtherT, class =
19 std::enable_if_t<std::is_convertible_v<OtherT*, T*>>>
20 slist_iter(const slist_iter<OtherT>& other) : node_(other.node_) {}
21 reference operator*() {return *node_;}
22 pointer operator->() {return node_;}
23 slist_iter& operator++() {
24 node_ = node_->next;
25 return *this;
26 }
[link: SFINAE]
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1173
Forward Iterator Example: Iterator Class Without Boost (2)
27 slist_iter operator++(int) {
28 slist_iter old(*this);
29 node_ = node_->next;
30 return old;
31 }
32 template <class OtherT> bool operator==(const slist_iter<OtherT>& other)
33 const {return node_ == other.node_;}
34 template <class OtherT> bool operator!=(const slist_iter<OtherT>& other)
35 const {return !(*this == other);}
36 private:
37 template <class> friend class slist_iter;
38 T* node_; // pointer to list node
39 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1174
Forward Iterator Example: Iterator Class With Boost
1 #include <type_traits>
2 #include <boost/iterator/iterator_facade.hpp>
3
4 template <class T> struct slist_node_base {
5 slist_node_base(T* next_) : next(next_) {}
6 T* next; // pointer to next node in list
7 };
8
9 template <class T> class slist_iter : public boost::iterator_facade<
10 slist_iter<T>, T, boost::forward_traversal_tag> {
11 public:
12 using base = typename boost::iterator_facade<slist_iter<T>, T,
13 boost::forward_traversal_tag>;
14 using typename base::reference;
15 using typename base::value_type;
16 slist_iter(T* node = nullptr) : node_(node) {}
17 template <class OtherT, class =
18 std::enable_if_t<std::is_convertible_v<OtherT*, T*>>>
19 slist_iter(const slist_iter<OtherT>& other) : node_(other.node_) {}
20 private:
21 reference dereference() const {return *node_;}
22 template <class OtherT> bool equal(const slist_iter<OtherT>& other) const
23 {return node_ == other.node_;}
24 void increment() {node_ = node_->next;}
25 template <class> friend class slist_iter;
26 friend class boost::iterator_core_access;
27 T* node_; // pointer to list node
28 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1175
Forward Iterator Example: User Code
1 #include <iostream>
2 #include <vector>
3 #include "iterator_facade_2.hpp"
4
5 struct Node : public slist_node_base<Node> {
6 Node(Node* next_, int value_) : slist_node_base<Node>(next_),
7 value(value_) {}
8 int value;
9 };
10
11 int main() {
12 constexpr int num_nodes = 10;
13 std::vector<Node> nodes; nodes.reserve(num_nodes);
14 for (int i = 0; i < num_nodes - 1; ++i)
15 {nodes.push_back(Node(&nodes[i + 1], i));}
16 nodes.push_back(Node(nullptr, num_nodes - 1));
17 slist_iter<Node> begin(&nodes[0]);
18 slist_iter<Node> end;
19 slist_iter<const Node> cbegin(begin);
20 slist_iter<const Node> cend(end);
21 for (auto i = cbegin; i != cend; ++i) {std::cout << i->value << ’\n’;}
22 slist_iter<Node> i(begin);
23 slist_iter<const Node> ci(cbegin);
24 // slist_iter<Node> j(cbegin); // ERROR
25 i = begin;
26 // i = ci; // ERROR
27 ci = cbegin;
28 ci = i;
29 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1176
Random-Access Iterator Example: Iterator Class Without Boost (1)
1 #include <type_traits>
2 #include <iterator>
3
4 // array element iterator
5 template <class T> class array_iter {
6 public:
7 using iterator_category = typename std::random_access_iterator_tag;
8 using value_type = std::remove_const_t<T>;
9 using reference = T&;
10 using pointer = T*;
11 using difference_type = std::ptrdiff_t;
12 array_iter(T* ptr = nullptr) : ptr_(ptr) {}
13 template <class OtherT, class =
14 std::enable_if_t<std::is_convertible_v<OtherT*, T*>>>
15 array_iter(const array_iter<OtherT>& other) : ptr_(other.ptr_) {}
16 reference operator*() const {return *ptr_;}
17 pointer operator->() const {return ptr_;}
18 array_iter& operator++() {
19 ++ptr_;
20 return *this;
21 }
22 array_iter operator++(int) {
23 array_iter old(*this);
24 ++ptr_;
25 return old;
26 }
27 array_iter& operator--() {
28 --ptr_;
29 return *this;
30 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1177
Random-Access Iterator Example: Iterator Class Without Boost (2)
31 array_iter operator--(int) {
32 array_iter old(*this);
33 --ptr_;
34 return old;
35 }
36 array_iter& operator+=(difference_type n) {
37 ptr_ += n;
38 return *this;
39 }
40 array_iter& operator-=(difference_type n) {
41 ptr_ -= n;
42 return *this;
43 }
44 reference& operator[](difference_type n) const {return ptr_[n];}
45 array_iter operator+(difference_type n) const
46 {return array_iter(ptr_ + n);}
47 difference_type operator-(const array_iter& other) const
48 {return ptr_ - other.ptr_;}
49 array_iter operator-(difference_type n) const
50 {return array_iter(ptr_ - n);}
51 template <class OtherT> bool operator==(const array_iter<OtherT>& other)
52 const {return ptr_ == other.ptr_;}
53 template <class OtherT> bool operator!=(const array_iter<OtherT>& other)
54 const {return ptr_ != other.ptr_;}
55 template <class OtherT> bool operator<(const array_iter<OtherT>& other)
56 const {return ptr_ < other.ptr_;}
57 template <class OtherT> bool operator>(const array_iter<OtherT>& other)
58 const {return ptr_ > other.ptr_;}
59 template <class OtherT> bool operator<=(const array_iter<OtherT>& other)
60 const {return ptr_ <= other.ptr_;}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1178
Random-Access Iterator Example: Iterator Class Without Boost (3)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1179
Random-Access Iterator Example: Iterator Class With Boost
1 #include <boost/iterator/iterator_facade.hpp>
2 #include <type_traits>
3
4 // array element iterator
5 template <class T> class array_iter : public boost::iterator_facade<
6 array_iter<T>, T, boost::random_access_traversal_tag> {
7 public:
8 using typename boost::iterator_facade<array_iter<T>, T,
9 boost::random_access_traversal_tag>::reference;
10 using typename boost::iterator_facade<array_iter<T>, T,
11 boost::random_access_traversal_tag>::difference_type;
12 array_iter(T* ptr = nullptr) : ptr_(ptr) {}
13 template <class OtherT, class =
14 std::enable_if_t<std::is_convertible_v<OtherT*, T*>>>
15 array_iter(const array_iter<OtherT>& other) : ptr_(other.ptr_) {}
16 private:
17 reference dereference() const {return *ptr_;}
18 template <class OtherT> bool equal(const array_iter<OtherT>& other) const
19 {return ptr_ == other.ptr_;}
20 void increment() {++ptr_;}
21 void decrement() {--ptr_;}
22 void advance(difference_type n) {ptr_ += n;}
23 difference_type distance_to(const array_iter& other) const
24 {return other.ptr_ - ptr_;}
25 template <class> friend class array_iter;
26 friend class boost::iterator_core_access;
27 T* ptr_; // pointer to array element
28 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1180
Random-Access Iterator Example: User Code
1 #include <iostream>
2 #include <cassert>
3 #include "iterator_facade_1.hpp"
4
5 int main() {
6 char buffer[] = "Hello, World!\n";
7 std::size_t length = sizeof(buffer) - 1;
8 array_iter<char> begin(buffer);
9 array_iter<char> end(buffer + length);
10 array_iter<const char> cbegin = begin;
11 array_iter<const char> cend = end;
12 assert(begin + length == end);
13 assert(cbegin + length == end);
14 for (auto i = cbegin; i != cend; ++i)
15 {std::cout << *i << ’\n’;}
16 array_iter<char> i(begin);
17 array_iter<const char> ci(cbegin);
18 // array_iter<char> j(cbegin); // ERROR
19 i = begin;
20 // i = ci; // ERROR
21 ci = cbegin;
22 ci = i;
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1181
Section 5.1.5
Miscellaneous Examples
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1182
Math Constants π and e
1 #include <iostream>
2 #include <limits>
3 #include <boost/math/constants/constants.hpp>
4
5 int main() {
6 namespace bmc = boost::math::constants;
7
8 std::cout.precision(std::numeric_limits<
9 long double>::max_digits10);
10
11 constexpr auto f_pi = bmc::pi<float>();
12 constexpr auto d_pi = bmc::pi<double>();
13 constexpr auto ld_pi = bmc::pi<long double>();
14
15 std::cout << f_pi << ’\n’;
16 std::cout << d_pi << ’\n’;
17 std::cout << ld_pi << ’\n’;
18
19 constexpr auto f_e = bmc::e<float>();
20 constexpr auto d_e = bmc::e<double>();
21 constexpr auto ld_e = bmc::e<long double>();
22
23 std::cout << f_e << ’\n’;
24 std::cout << d_e << ’\n’;
25 std::cout << ld_e << ’\n’;
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1183
Math Constant π
1 #include <iostream>
2 #include <boost/math/constants/constants.hpp>
3
4 template <class Real>
5 Real area_of_circle(Real r) {
6 namespace bmc = boost::math::constants;
7 return bmc::pi<Real>() * r * r;
8 }
9
10 int main() {
11 double r;
12 while (std::cin >> r) {
13 std::cout << area_of_circle(r) << ’\n’;
14 }
15 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1184
Computing Factorials With Arbitrary Precision
1 #include <cmath>
2 #include <boost/multiprecision/gmp.hpp>
3 #include <iostream>
4
5 using boost::multiprecision::mpz_int;
6
7 mpz_int factorial(const mpz_int& n) {
8 mpz_int result = 1;
9 for (mpz_int i = n; i >= 2; --i) {
10 result *= i;
11 }
12 return result;
13 }
14
15 int main() {
16 std::cout << factorial(200) << ’\n’;
17 }
18
19 /* Output:
20 788657867364790503552363213932185062295135977687173263294
21 742533244359449963403342920304284011984623904177212138919
22 638830257642790242637105061926624952829931113462857270763
23 317237396988943922445621451664240254033291864131227428294
24 853277524242407573903240321257405579568660226031904170324
25 062351700858796178922222789623703897374720000000000000000
26 000000000000000000000000000000000
27 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1185
multi_array Example
1 #include <boost/multi_array.hpp>
2 #include <iostream>
3 #include <iomanip>
4
5 int main() {
6 using Array2 = boost::multi_array<int, 2>;
7 int num_rows = 5;
8 int num_cols = 7;
9 Array2 a(boost::extents[num_rows][num_cols]);
10 for (int row = 0; row < num_rows; ++row) {
11 for (int col = 0; col < num_cols; ++col) {
12 a[row][col] = num_cols * row + col;
13 }
14 }
15 Array2 b(a);
16 assert(b.shape()[0] == num_rows && b.shape()[1] == num_cols);
17 Array2 c;
18 c.resize(boost::extents[b.shape()[0]][b.shape()[1]]);
19 c = b;
20 for (int row = 0; row < num_rows; ++row) {
21 for (int col = 0; col < num_cols; ++col) {
22 if (col) {std::cout << ’ ’;}
23 std::cout << std::setw(2) << c[row][col];
24 }
25 std::cout << ’\n’;
26 }
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1186
2-D Array Class With multi_array
1 #include <boost/multi_array.hpp>
2 #include <iostream>
3 #include <iomanip>
4
5 template <class T>
6 class array2 {
7 public:
8 using value_type = T;
9 array2(int num_rows = 0, int num_cols = 0) :
10 a_(boost::extents[num_rows][num_cols]) {}
11 array2(const array2& other) : a_(other.a_) {}
12 array2& operator=(const array2& other) {
13 if (this != &other) {
14 a_.resize(boost::extents[other.a_.shape()[0]][
15 other.a_.shape()[1]]);
16 a_ = other.a_;
17 }
18 return *this;
19 }
20 int num_rows() const {return a_.shape()[0];}
21 int num_cols() const {return a_.shape()[1];}
22 const value_type& operator()(int row, int col) const
23 {return a_[row][col];}
24 value_type& operator()(int row, int col) {return a_[row][col];}
25 private:
26 using array = boost::multi_array<T, 2>;
27 array a_;
28 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1187
2-D Array Class With multi_array (Continued)
29
30 template <class T>
31 std::ostream& operator<<(std::ostream& out, const array2<T>& a) {
32 auto width = out.width();
33 for (int row = 0; row < a.num_rows(); ++row) {
34 for (int col = 0; col < a.num_cols(); ++col) {
35 if (col) {out << ’ ’;}
36 out << std::setw(width) << a(row, col);
37 }
38 out << ’\n’;
39 }
40 return out;
41 }
42
43 int main() {
44 array2<int> a(5, 7);
45 for (int row = 0; row < a.num_rows(); ++row) {
46 for (int col = 0; col < a.num_cols(); ++col) {
47 a(row, col) = a.num_cols() * row + col;
48 }
49 }
50 array2<int> b(a);
51 std::cout << "a:\n" << std::setw(2) << a;
52 std::cout << "b:\n" << std::setw(2) << b;
53 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1188
Program Options Example
1 #include <iostream>
2 #include <string>
3 #include <boost/program_options.hpp>
4
5 int main(int argc, char** argv) {
6 namespace po = boost::program_options;
7 po::options_description desc("Allowed options");
8 desc.add_options()
9 ("help,h", "Print help information.")
10 ("count,c", po::value<int>()->default_value(1), "Specify count.")
11 ("file,f", po::value<std::string>(), "Specify file name.");
12 po::variables_map vm;
13 try {
14 po::store(po::parse_command_line(argc, argv, desc), vm);
15 po::notify(vm);
16 } catch (po::error& e) {
17 std::cerr << "usage:\n" << desc << ’\n’;
18 return 1;
19 }
20 if (vm.count("help")) {std::cout << desc << "\n"; return 1;}
21 if (vm.count("file")) {
22 std::cout << "file: " << vm["file"].as<std::string>() << ’\n’;
23 }
24 if (vm.count("count")) {
25 std::cout << "count: " << vm["count"].as<int>() << ’\n’;
26 }
27 return 0;
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1189
Rational Numbers Example
1 #include <iostream>
2 #include <cassert>
3 #include <boost/rational.hpp>
4 #include <exception>
5
6 int main() {
7 using boost::rational;
8 const rational<int> zero;
9 rational<int> three(3);
10 rational<int> ninth(1, 9);
11 rational<int> third(1, 3);
12 auto result = three * ninth;
13 assert(result == third);
14 try {
15 std::cout << three / zero << ’\n’;
16 } catch (const boost::bad_rational& e) {
17 std::cout << "bad rational " << e.what() << ’\n’;
18 }
19 // rational<int> x(1.5); // ERROR: no matching call
20 // result = 3.0; // ERROR: no matching call
21 result = 42;
22 assert(result == rational<int>(42));
23 std::cout << result << ’\n’;
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1190
Section 5.1.6
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1191
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1192
Talks I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1193
Section 5.2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1194
Computational Geometry Algorithms Library (CGAL)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1196
Handles
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1197
Linear Sequences Versus Circular Sequences
f a
c d
a d g b e h c f
Linear Sequence h g
e b
Circular Sequence
linear sequence:
has well defined first and last element
fits well with iterator model
circular sequence:
does not have well defined first and last element
does not fit well with iterator model
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1198
Circulators
iterators are very useful, but intended for use with linear sequences of
elements (i.e., sequences with well-defined first and last element)
often want iterator-like functionality for circular sequences of elements
circulator: object that allows iteration over elements in circular sequence
of elements
examples of circulator types:
type to allow iteration over all halfedges incident on vertex in polygon mesh
type to allow iteration over all halfedges incident on facet in polygon mesh
circulators come in const and mutable (i.e., non-const) forms
mutable circulator can be used to modify referenced element, while const
circulator cannot
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1199
Section 5.2.1
Geometry Kernels
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1200
Real Number Types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1201
MP_Float Class
MP_Float is arbitrary-precision floating-point type
additions, subtractions, and multiplications computed exactly
does not provide division or square root (which is not typically problematic
as division rarely needed and square root almost always avoided in
geometric computation)
no roundoff error
no overflow error unless astronomically large numbers involved (arbitrary
length mantissa; integral-valued double exponent can overflow, but
extremely unlikely)
very slow, can require considerable memory (unbounded)
default constructor does not initialize to particular value
stream inserter (i.e., operator<<) for MP_Float first converts
MP_Float to double and then outputs result
stream extracter (i.e., operator>>) for MP_Float first reads double
and then converts to MP_Float
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1202
MP_Float Example
1 #include <iostream>
2 #include <CGAL/MP_Float.h>
3
4 int main() {
5 CGAL::MP_Float x;
6 CGAL::MP_Float y;
7 if (!(std::cin >> x >> y)) {return 1;}
8 if (x < y) {
9 std::cout << x << " is less than " << y << ’\n’;
10 }
11 CGAL::MP_Float z = -(x + y) * (x - y) + x;
12 std::cout << z << ’\n’;
13 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1203
Interval_nt Class
declared as: template <bool M = true> Interval_nt<M>
M indicates if safe rounding mode enabled
if safe rounding mode enabled, rounding mode always restored to round
towards zero (required by C++); must be careful if safe rounding mode not
used
when safe rounding mode not used, faster but need to worry about things
like compiler options like -frounding-math
using Interval_nt_advanced = Interval_nt<false>; (i.e.,
Interval_nt_advanced is Interval_nt with safe rounding mode
disabled)
interval-arithmetic number type (internally uses floating-point type)
represents interval [a, b]
every arithmetic operation performed twice, once while rounding towards
−∞ to produce result a′ and once while rounding towards +∞ to produce
result b′
true answer must lie on interval [a′ , b′ ]
approximately twice of time cost of built-in floating-point type
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1204
Geometry Kernels
represent geometric objects (e.g., point, line, line segment, ray, plane,
triangle, circle, )
points in 2 or 3 dimensions
provide operations on geometric objects (e.g., intersection, composition)
allow certain conditions to be tested involving geometric objects (e.g.,
collinear, coplanar, equality)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1205
Point Representation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1206
Simple_cartesian and Cartesian Classes
declaration:
template <class F> Cartesian<F>
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1207
Simple_homogeneous and Homogeneous Classes
declaration:
template <class R> Homogeneous<R>
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1208
Constructions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1209
Predicates
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1210
Kernel Member Types: Basic Types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1211
Kernel Member Types: Geometric Objects in Two Dimensions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1212
Kernel Member Types: Geometric Objects in Three Dimensions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1213
Kernel Selection
coordinate representation
exact or inexact constructions
exact or inexact predicates
in practice, almost always require exact predicates
if code well designed, need for exact constructions can usually be avoided
for T chosen as any numeric type that has roundoff/overflow error (e.g.,
float, double, long double), the following kernels do not provide
exact constructions or exact predicates:
Simple_cartesian<T>
Cartesian<T>
Simple_homogeneous<T>
Homogeneous<T>
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1214
Filtered_kernel Class
class to convert kernel with inexact predicates into one with exact
predicates
declared as:
template <class K> Filtered_kernel<K>
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1215
Writing Custom Exact Predicates
exact predicate cannot at any point rely on a computation that is not exact
no floating point arithmetic (since it has roundoff error)
no integer arithmetic that might overflow
no inexact constructions
no inexact predicates
Filtered_predicate may be helpful
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1216
Filtered_predicate Class
adapter for predicate functors for producing efficient exact predicates
declared as:
template <class EP, class FP, class CE, class CF>
Filtered_predicate<EP, FP, CE, CF>
EP is exact predicate (typically uses arbitrary-precision type such as
MP_Float)
FP is filtering predicate (typically uses interval-arithmetic type like
Interval_nt)
CE and CF are function objects for converting arguments of unfiltered
predicate to types used by exact and filtering predicates
must be careful about operation used in unfiltered predicate being
plugged into Filtered_kernel
for kernel ring number type RT, can safely use addition, subtraction,
multiplication
can also safely use sign
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1217
Execution of Filtered Predicate
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1218
Filtered Predicate Example
1 #include <CGAL/Cartesian.h>
2 #include <CGAL/MP_Float.h>
3 #include <CGAL/Interval_nt.h>
4 #include <CGAL/Filtered_predicate.h>
5 #include <CGAL/Cartesian_converter.h>
6
7 template <class K>
8 struct Test_orientation_2 {
9 using RT = typename K::RT;
10 using Point_2 = typename K::Point_2;
11 using result_type = typename K::Orientation;
12 result_type operator()(const Point_2& p, const Point_2& q,
13 const Point_2& r) const {
14 RT prx = p.x() - r.x();
15 RT pry = p.y() - r.y();
16 RT qrx = q.x() - r.x();
17 RT qry = q.y() - r.y();
18 return CGAL::sign(prx * qry - qrx * pry);
19 }
20 };
21
22 using Kernel = CGAL::Cartesian<double>;
23 using Ia_kernel = CGAL::Cartesian<CGAL::Interval_nt<false>>;
24 using Exact_kernel = CGAL::Cartesian<CGAL::MP_Float>;
25 using Test_orientation = CGAL::Filtered_predicate<
26 Test_orientation_2<Exact_kernel>,
27 Test_orientation_2<Ia_kernel>,
28 CGAL::Cartesian_converter<Kernel, Exact_kernel>,
29 CGAL::Cartesian_converter<Kernel, Ia_kernel>
30 >;
31
32 int main() {
33 double big = 1e50;
34 Kernel::Point_2 p(0.0, 0.0), q(1.0, 1.0), r(2.0 * big, 2.0 * big);
35 Test_orientation orientation;
36 std::cout << orientation(p, q, r) << "\n";
37 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1219
Filtered Predicate Example (Continued)
Polygon Meshes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1221
Polyhedron_3 Class
represents polyhedral surface (i.e., polygon mesh), which consists of
vertices, edges, and facets and incidence relationship amongst them
each edge represented by pair of halfedges
declaration for Polyhedron_3 class:
template <class Kernel,
class PolyhedronItems = CGAL::Polyhedron_items_3,
template <class T, class I>
class HalfedgeDS = CGAL::HalfedgeDS_default,
class Alloc = CGAL_ALLOCATOR(int)>
class Polyhedron_3;
Kernel is geometry kernel, which specifies such things as how points are
represented and provides basic geometric operations/predicates (e.g.,
CGAL::Cartesian<double> and
CGAL::Filtered_kernel<CGAL::Cartesian<double>>)
PolyhedronItems specifies data types for representing vertices and
facets (in many cases, default will suffice)
HalfedgeDS specifies halfedge data structure for representing polygon
mesh and Alloc specifies allocator (defaults should suffice)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1222
Polyhedron_3 Type Members
Basic Types
Type Description
Vertex vertex type
Halfedge halfedge type
Facet facet type
Point_3 point type (for vertices)
Handles
Type Description
Vertex_const_handle const handle to vertex
Vertex_handle handle to vertex
Halfedge_const_handle const handle to halfedge
Halfedge_handle handle to halfedge
Facet_const_handle const handle to facet
Facet_handle handle to facet
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1223
Polyhedron_3 Type Members (Continued 1)
Iterators
Type Description
Vertex_const_iterator const iterator over all vertices
Vertex_iterator iterator over all vertices
Halfedge_const_iterator const iterator over all halfedges
Halfedge_iterator iterator over all halfedges
Facet_const_iterator const iterator over all facets
Facet_iterator iterator over all facets
Edge_const_iterator const iterator over all edges (ev-
ery other halfedge)
Edge_iterator iterator over all edges (every
other halfedge)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1224
Polyhedron_3 Type Members (Continued 2)
Circulators
Type Description
Halfedge_around_vertex_const_circulator const circulator of halfedges
around vertex (CW)
Halfedge_around_vertex_circulator circulator of halfedges
around vertex (CW)
Halfedge_around_facet_const_circulator const circulator of halfedges
around facet (CCW)
Halfedge_around_facet_circulator circulator of halfedges
around facet (CCW)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1225
Polyhedron_3 Function Members
Size
Name Description
size_of_vertices get number of vertices
size_of_halfedges get number of halfedges
size_of_facets get number of facets
Iterators
Name Description
vertices_begin iterator for first vertex in mesh
vertices_end past-the-end vertex iterator
halfedges_begin iterator for first halfedge in mesh
halfedges_end past-the-end halfedge iterator
facets_begin iterator for first facet in mesh
facets_end past-the-end facet iterator
edges_begin iterator for first edge in mesh
edges_end past-the-end edge iterator
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1226
Polyhedron_3 Function Members (Continued 1)
Combinatorial Predicates
Name Description
is_closed true if no border edges (no boundary)
is_pure_triangle true if all facets are triangles
is_pure_quad true if all facets are quadrilaterals
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1227
Polyhedron_3 Function Members (Continued 2)
Border Halfedges
Name Description
normalized_border_is_valid true if border is normalized
normalize_border sort halfedges such that non-
border edges precede border
edges (i.e., normalize border)
size_of_border_halfedges get number of border halfedges
(border must be normalized)
size_of_border_edges get number of border edges
(border must be normalized)
border_halfedges_begin halfedge iterator starting with
border edges (border must be
normalized)
border_edges_begin edge iterator starting with border
edges (border must be normal-
ized)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1228
Polyhedron_3::Facet
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1229
Facet Function Members
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1230
Polyhedron_3::Vertex
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1231
Vertex Function Members
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1232
Polyhedron_3::Halfedge
Halfedge type represents halfedge in polyhedral surface
actual class type to which Halfedge corresponds depends on choice of
PolyhedronItems template parameter for Polyhedron_3 class
depending on actual class type to which Halfedge refers, level of
functionality offered by Halfedge class may differ (e.g., available function
members may differ)
each halfedge directly associated with one vertex and one facet, referred
to as incident vertex and incident facet, respectively
incident vertex is vertex at terminal end of halfedge
incident facet is facet to left of halfedge
Incident Facet
vertex
Incident Vertex
halfedge
facet
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1233
Polyhedron_3::Halfedge (Continued)
halfedge contains:
handle for next halfedge around incident facet in CCW direction
handle for opposite halfedge
together, these two handles allow for efficient iteration around:
halfedges incident on facet in CCW direction only; and
Circulators
Name Description
vertex_begin get halfedge-around-vertex circulator for incident vertex
(CW order)
facet_begin get halfedge-around-facet circulator for incident facet
(CCW order)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1235
Halfedge Function Members (Continued 1)
Border Queries
Name Description
is_border true if border halfedge
is_border_edge true if associated edge on border
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1236
Halfedge Function Members (Continued 2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1237
Adjacency Example
h->next_on_vertex()
h->facet()
h->prev() h->next()
h
h->opposite()-> h->vertex()
vertex()
h->opposite()
h->prev_on_vertex()
h->opposite()->
facet()
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1238
Polyhedron_3 I/O
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1239
Polyhedron_3 Gotchas
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1240
Section 5.2.3
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1241
Subdivision Methods
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1242
Subdivision Functions
Generic Subdivision Methods
Function Description
PQQ perform primal quadrilateral quadrisection with arbitrary
geometric refinement rule
PTQ perform primal triangle quadrisection with arbitrary geo-
metric refinement rule
DQQ perform dual quadrilateral quadrisection with arbitrary
geometric refinement rule
√
Sqrt3 perform 3 topologic refinement with arbitrary geometric
refinement rule
Example Programs
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1244
Mesh Generation Program: meshMake
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1245
Mesh Information Program: meshInfo
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1246
Mesh Subdivision Program: meshSubdivide
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1247
Section 5.3
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1248
OpenGL Utility Toolkit (GLUT)
simple windowing API for OpenGL
intended to be used with small to medium sized OpenGL programs
language binding for C
window-system independent
supports most mainstream operating systems (Microsoft Windows,
Linux/Unix)
provides window management functionality (e.g., creating/destroying
windows, displaying/resizing windows, and querying/setting window
attributes)
allows for user input (e.g., via keyboard, mouse)
routines for drawing common wireframe/solid 3-D objects such as sphere,
torus, and well-known teapot model
register callback functions to handle various types of events (e.g., display,
resize, keyboard, special keyboard, mouse, timer, idle) and then loop
processing events
open-source implementation of GLUT called Freeglut is available from
http://sourceforge.net/projects/freeglut
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1249
Event-Driven Model
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1250
Structure of GLUT Application
register callback functions for handling various types of events (e.g., via
glutDisplayFunc, glutReshapeFunc, glutKeyboardFunc)
setup initial OpenGL state (e.g., depth buffering, shading, lighting, clear
color)
4 enter main event-processing loop by calling glutMainLoop [Note that
glutMainLoop never returns.]
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1251
GLUT Header Files
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1252
Event Types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1253
Event Types (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1254
Functions
Initialization
Function Description
glutInit initialize GLUT library
glutInitWindowSize set initial window size for
glutCreateWindow
glutInitWindowPosition set initial window position for
glutCreateWindow
glutInitDisplayMode set initial display mode
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1255
Functions (Continued 1)
Window Management
Function Description
glutCreateWindow create top-level window
glutCreateSubWindow create subwindow
glutSetWindow set current window
glutGetWindow get current window
glutDestroyWindow destroys specified window
glutPostRedisplay mark current window as needing to be redisplayed
glutSwapBuffers swaps buffers of current window if double buffered
(flushes graphics output via glFlush)
glutPositionWindow request change to position of current window
glutReshapeWindow request change to size of current window
glutFullScreen request current window to be made full screen
glutSetWindowTitle set title of current top-level window
glutSetIconTitle set title of icon for current top-level window
glutSetCursor set cursor image for current window
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1256
Functions (Continued 2)
Menu Management
Function Description
glutCreateMenu create new pop-up menu
glutSetMenu set current menu
glutGetMenu get current menu
glutDestroyMenu destroy specified menu
glutAddMenuEntry add menu entry to bottom of current menu
glutAddSubMenu add submenu trigger to bottom of current
menu
glutChangeToMenuEntry change specified menu item in current menu
into menu entry
glutChangeToSubMenu change specified menu item in current menu
into submenu trigger
glutRemoveMenuItem remove specified menu item
glutAttachMenu attach mouse button for current window to cur-
rent menu
glutDetachMenu detach attached mouse button from current
window
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1257
Functions (Continued 3)
Callback Registration
Function Description
glutDisplayFunc sets display callback for current window
glutReshapeFunc sets reshape callback for current window
glutKeyboardFunc sets keyboard callback for current window
glutMouseFunc sets mouse callback for current window
glutMotionFunc set motion callback for current window
glutPassiveMotionFunc set passive motion callback for current win-
dow
glutVisibilityFunc set visibility callback for current window
glutEntryFunc set mouse enter/leave callback for current
window
glutSpecialFunc sets special keyboard callback for current
window
glutIdleFunc set global idle callback
glutTimerFunc registers timer callback to be triggered in
specified number of milliseconds
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1258
Functions (Continued 4)
State Retrieval
Function Description
glutGet retrieves simple GLUT state (e.g., size or position
of current window)
glutDeviceGet retrieves GLUT device information (e.g., keyboard,
mouse, spaceball, tablet)
glutGetModifiers retrieve modifier key state when certain callbacks
generated (i.e., state of shift, control, and alt keys)
Font Rendering
Function Description
glutBitmapCharacter renders bitmap character using OpenGL
glutBitmapWidth get width of bitmap character
glutStrokeCharacter renders stroke character using OpenGL
glutStrokeWidth get width of stroke character
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1259
Functions (Continued 5)
Geometric Object Rendering
Function Description
glutSolidSphere render solid sphere
glutWireSphere render wireframe sphere
glutSolidCube render solid cube
glutWireCube render wireframe cube
glutSolidCone render solid cone
glutWireCone render wireframe cone
glutSolidTorus render solid torus
glutWireTorus render wireframe torus
glutSolidOctahedron render solid octahedron
glutWireOctahedron render wireframe octahedron
glutSolidTetrahedron render solid tetrahedron
glutWireTetrahedron render wireframe tetrahedron
glutSolidTeapot render solid teapot
glutWireTeapot render wireframe teapot
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1260
Minimalist GLUT Program
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1261
Minimalist GLUT Program: Source Code
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1262
GLUT References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1263
Section 5.4
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1264
OpenGL Framework (GLFW) Library
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1265
GLFW Versus GLUT
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1266
Event-Driven Model
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1267
Structure of GLFW Application
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1268
GLFW Header Files
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1269
Event Types
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1270
Event Types (Continued 1)
Other Events
Event Type Description
error error has occurred in GLFW library
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1271
Functions
Initialization and Termination
Function Description
glfwInit initialize GLFW library
glfwTerminate cleanup GLFW library
Version
Function Description
glfwGetVersion get version of GLFW library
glfwGetVersionString get version string of GLFW library
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1272
Functions (Continued 1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1273
Functions (Continued 2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1274
Functions (Continued 3)
Window Management
Function Description
glfwIconifyWindow iconifies specified window
glfwRestoreWindow restores (i.e., deiconifies) specified window
glfwMaximizeWindow maximizes specified window
glfwShowWindow make specified window visible
glfwHideWindow hide specified window
glfwFocusWindow bring specified window to front and give it input
focus
glfwSwapBuffers swap front and back buffers of specified win-
dow when rendering with OpenGL or OpenGL
ES
glfwSwapInterval set swap interval for current OpenGL or
OpenGL ES context
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1275
Functions (Continued 4)
Callback Registration
Function Description
glfwSetErrorCallback sets error callback function
glfwSetWindowPosCallback sets window-position callback function
for specified window
glfwSetWindowSizeCallback sets window-size callback function for
specified window
glfwSetWindowCloseCallback sets window-close callback function for
specified window
glfwSetWindowRefreshCallback sets window-refresh callback function
for specified window
glfwSetWindowFocusCallback sets window-focus callback function for
specified window
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1276
Functions (Continued 5)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1277
Functions (Continued 6)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1278
Functions (Continued 7)
Event Handling
Function Description
glfwPostEmptyEvent post empty event to event queue
glfwPollEvents process any pending events and return imme-
diately
glfwWaitEvents wait until at least one event is pending, then
process all pending events and return
glfwWaitEventsTimeout wait until at least one event pending or timeout
expires, then process any pending events and
return
Timing
Function Description
glfwGetTime get value of timer in seconds
glfwSetTime set value of timer
glfwGetTimerValue get value of timer in clock ticks
glfwGetTimerFrequency get frequency of clock tick
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1279
Functions (Continued 8)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1280
Functions (Continued 9)
Clipboard
Function Description
glfwGetClipboardString gets contents of clipboard as string
glfwSetClipboardString sets clipboard to specified string
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1281
Functions (Continued 10)
Monitor Management
Function Description
glfwGetMonitors get currently connected monitors
glfwGetPrimaryMonitor get primary monitor
glfwGetMonitorPos get position of specified monitor’s viewport on
virtual screen
glfwGetMonitorPhysicalSize get physical size of specified monitor
glfwGetMonitorName get name of specified monitor
glfwGetVideoModes get available video modes for specified monitor
glfwGetVideoMode get current video mode of specified monitor
glfwSetGamma set gamma for specified monitor
glfwGetGammaRamp get current gamma ramp for specified monitor
glfwSetGammaRamp set current gamma ramp for specified monitor
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1282
Functions (Continued 11)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1283
Functions (Continued 12)
Vulkan
Function Description
glfwVulkanSupported tests if Vulkan loader has
been found
glfwGetRequiredInstanceExtensions get Vulkan instance exten-
sions required by GLFW
glfwGetInstanceProcAddress get address of specified
Vulkan instance function
glfwGetPhysicalDevicePresentationSupport test if specified queue fam-
ily can present images
glfwCreateWindowSurface create Vulkan surface for
specified Window
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1284
Minimalist GLFW Program
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1285
Minimalist GLFW Program: Source Code
1 #include <cstdlib>
2 #include <GLFW/glfw3.h>
3
4 void display(GLFWwindow* window) {
5 glClearColor(0.0, 1.0, 1.0, 0.0);
6 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
7 glfwSwapBuffers(window);
8 }
9
10 int main(int argc, char** argv) {
11 if (!glfwInit()) {return EXIT_FAILURE;}
12 glfwSwapInterval(1);
13 GLFWwindow* window = glfwCreateWindow(512, 512, argv[0],
14 nullptr, nullptr);
15 if (!window) {
16 glfwTerminate();
17 return EXIT_FAILURE;
18 }
19 glfwMakeContextCurrent(window);
20 glfwSetWindowRefreshCallback(window, display);
21 while (!glfwWindowShouldClose(window)) {
22 glfwWaitEvents();
23 }
24 glfwTerminate();
25 return EXIT_SUCCESS;
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1286
GLFW References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1287
Section 5.5
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1288
OpenGL Mathematics (GLM) Library
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1289
GLM Header Files
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1290
Types
matrix types:
mat2x2, mat2x3, mat2x4, mat2,
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1291
Functions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1292
Code Example: Basic Usage
1 #include <iostream>
2 #include <glm/glm.hpp>
3 #include <glm/gtc/matrix_transform.hpp>
4 #include <glm/gtx/string_cast.hpp>
5 #include <cmath>
6
7 int main() {
8 glm::mat4 mv(1.0f);
9 mv = mv * glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f),
10 glm::vec3(1.0f, 0.0f, 0.0f),
11 glm::vec3(0.0f, 0.0f, 1.0f));
12 mv = mv * glm::translate(mv, glm::vec3(1.0f, 1.0f, 1.0f));
13 mv = mv * glm::rotate(mv, glm::radians(90.0f),
14 glm::vec3(0.0f, 0.0f, 1.0f));
15 mv = mv * glm::scale(mv, glm::vec3(1.0f, 1.0f, 2.0f));
16 glm::mat4 p = glm::perspective(glm::radians(90.0f), 1.0f,
17 1.0f, 2.0f);
18 glm::mat4 mvp = p * mv;
19 glm::vec4 v(1.0f, -1.0f, -1.0f, 1.0f);
20 std::cout << glm::to_string(glm::vec3(mv * v)) << ’\n’;
21 std::cout << glm::to_string(glm::vec3(mvp * v)) << ’\n’;
22 std::cout << glm::radians(180.0f) << ’\n’;
23 std::cout << glm::degrees(M_PI) << ’\n’;
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1293
Code Example: value_ptr
1 #include <GL/glew.h>
2 #include <GL/gl.h>
3 #include <glm/glm.hpp>
4 #include <glm/gtc/type_ptr.hpp>
5
6 void setUniform(GLint loc) {
7 glm::mat4 m(1.0f);
8 // ...
9 glUniform4fv(loc, 4, glm::value_ptr(m));
10 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1294
Section 5.6
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1295
Open Graphics Library (OpenGL)
CPU GPU
Memory Memory
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1298
OpenGL State Machine
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1299
Contexts and Profiles
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1301
Types
Type Description
GLboolean boolean
GLbyte 8-bit signed two’s complement integer
GLubyte 8-bit unsigned integer
GLchar 8-bit character
GLshort 16-bit signed two’s complement integer
GLushort 16-bit unsigned integer
GLint 32-bit signed two’s complement integer
GLuint 32-bit unsigned integer
GLfloat single-precision floating-point value
GLdouble double-precision floating-point value
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1302
Function Naming Conventions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1304
Geometric Primitives
v3 v2 v3 v2 v3 v2 v3 v2
v0 v1 v0 v1 v0 v1 v0 v1
points lines line strip line loop
v3
v4 v3 v2 v5 v3 v1
v4 v2
v0
v5 v0 v1 v4 v2 v0
v5
triangles triangle strip v1 , v6
triangle fan
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1305
Provoking Vertex
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1306
Vertex Array and Vertex Buffer Objects (VAOs and VBOs)
Vertex Buffer
Vertex Positions Vertex Normals
Object
Vertex Array
Vertex Attribute Vertex Attribute
Object
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1308
Vertex Buffer Objects (VBOs)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1309
Coordinate Systems
Object World Eye Clip
Coordinates Modelling Coordinates Viewing Coordinates Projection Coordinates
Transformation Transformation Transformation
Normalized
Device Window
Clipping and Coordinates Viewport Coordinates
Perspective Transformation
Division
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1311
Transformations
Normalized
Device Window
Clipping and Coordinates Viewport Coordinates
Perspective Transformation
Division
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1312
Transformations (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1313
Depth Buffering
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1314
Face Culling
v2 v2
v0 v1 v1 v0
Counterclockwise (CCW) Clockwise (CW)
Winding Order Winding Order
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1315
State Management
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1316
Other Functions
Function Description
glClear clear buffer to preset values
glClearColor specify clear values for color buffers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1317
Program Structure
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1318
Section 5.6.1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1319
OpenGL Application Program Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1320
Header Files
1 #include <cstdlib>
2 #include <string>
3 #include <GL/glew.h>
4 #include <GLFW/glfw3.h>
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1321
Main Function
6 GLuint vao = 0;
103 void fatalError() {
104 glfwTerminate();
105 std::exit(EXIT_FAILURE);
106 }
108 int main(int argc, char** argv) {
109 if (!glfwInit()) {return EXIT_FAILURE;}
110 GLFWwindow* window = makeWindow(512, 512, argv[0]);
111 if (!window) {fatalError();}
112 glfwMakeContextCurrent(window);
113 glewExperimental = GL_TRUE;
114 if (glewInit() != GLEW_OK) {fatalError();}
115 GLuint program = makeProgram(vShaderSource,
116 fShaderSource);
117 if (!program) {fatalError();}
118 glUseProgram(program);
119 glClearColor(0.0, 0.0, 0.0, 0.0);
120 GLuint vbo;
121 makeVao(program, vao, vbo);
122 glfwSetWindowRefreshCallback(window, refresh);
123 while (!glfwWindowShouldClose(window))
124 {glfwWaitEvents();}
125 glfwTerminate();
126 return EXIT_SUCCESS;
127 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1322
Make Window
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1323
Vertex and Fragment Shaders
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1324
Compiling Shaders
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1325
Linking Shader Program
39 GLuint makeProgram(
40 const std::basic_string<GLchar>& vShaderSource,
41 const std::basic_string<GLchar>& fShaderSource) {
42 GLuint vShader = compileShader(GL_VERTEX_SHADER,
43 vShaderSource);
44 if (!vShader) {return 0;}
45 GLuint fShader = compileShader(GL_FRAGMENT_SHADER,
46 fShaderSource);
47 if (!fShader) {glDeleteShader(vShader); return 0;}
48 GLuint program = glCreateProgram();
49 GLint status = GL_FALSE;
50 if (program) {
51 glAttachShader(program, vShader);
52 glAttachShader(program, fShader);
53 glLinkProgram(program);
54 glGetProgramiv(program, GL_LINK_STATUS, &status);
55 }
56 glDeleteShader(vShader);
57 glDeleteShader(fShader);
58 if (!program) {return 0;}
59 if (status != GL_TRUE)
60 {glDeleteProgram(program); return 0;}
61 return program;
62 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1326
Initialize Vertex Array Object (VAO)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1327
Window Refresh Callback
6 GLuint vao = 0;
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1328
Section 5.6.2
Shaders
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1329
Shaders
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1330
Rendering Pipeline and Shaders
GPU
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1331
OpenGL Shader Language (GLSL)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1332
Reserved Keywords
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1333
Reserved Keywords (Continued)
samplerCube usamplerCube
sampler1DShadow usampler1DArray
sampler2DShadow usampler2DArray
samplerCubeShadow sampler2DRect
sampler1DArray sampler2DRectShadow
sampler2DArray isampler2DRect
sampler1DArrayShadow usampler2DRect
sampler2DArrayShadow samplerBuffer
isampler1D isamplerBuffer
isampler2D usamplerBuffer
isampler3D sampler2DMS
isamplerCube isampler2DMS
isampler1DArray usampler2DMS
isampler2DArray sampler2DMSArray
usampler1D isampler2DMSArray
usampler2D usampler2DMSArray
usampler3D struct
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1334
The #version Directive
#version directive specifies which version of GLSL should be used to
compile/link shader
if #version directive specified, must be first statement in source
if no #version directive given, version 1.10 is assumed
#version directive takes two parameters (with second being optional):
1 integer specifying GLSL version (scaled by a factor of 100)
2 profile name, which can be either core or compatibility with core being
default
for OpenGL 3.3 and above, corresponding GLSL version matches
OpenGL version (e.g., OpenGL 4.1 uses GLSL 4.1); for earlier OpenGL
versions, relationship between OpenGL and GLSL versions as follows:
OpenGL Version GLSL Version
2.0 1.10
2.1 1.20
3.0 1.30
3.1 1.40
3.2 1.50
for example, to specify use of GLSL 3.30 with core profile:
#version 330
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1335
Basic Types
Scalar and Void Types
Type Description
void dummy type for functions without return value
bool boolean type
int signed integer type
uint unsigned integer type
float single-precision floating-point type
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1336
Basic Types (Continued 1)
Vector of int Types
Type Description
ivec2 two-component vector of int
ivec3 three-component vector of int
ivec4 four-component vector of int
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1337
Basic Types (Continued 2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1338
Operators
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1339
Operators (Continued 1)
first, second, third, and fourth components of vector (if they exist) can be
selected by:
subscripting operator with subscripts 0, 1, 2, and 3, respectively; or
selection operator with x, y, z, and w, respectively; or
selection operator with r, g, b, and a, respectively; or
selection operator with s, t, p, and q, respectively
example:
vec3 v;
// ...
float x = v.x;
float y = v.y;
float z = v.z;
components of matrices can be accessed by subscripting operator
single subscripting on matrix results in column of matrix
double subscripting on matrix results in element of matrix
example:
mat2 a;
// ...
vec2 v = a[0];
float f = a[0][0];
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1340
Operators (Continued 2)
can also form vectors by selecting multiple elements from vector (e.g.,
swizzling and smearing)
example:
vec4 v; vec4 u;
vec3 a;
// ...
u = v.wzyx; // vec4(v.w, v.z, v.y, v.x)
u = v.xxyy; // vec4(v.x, v.x, v.y, v.y)
a = v.xyz; // vec3(v.x, v.y, v.z)
u = a.xxxx; // vec4(a.x, a.x, a.x, a.x)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1341
Control Flow
selection statements
if
if-else
ternary operator
switch
looping statements
for
while
do-while
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1342
Functions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1343
Constructors
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1344
Conversions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1346
Built-In Functions (Continued 1)
Exponential Functions
Function Description
pow exponentiation function
exp base-e exponentiation function
log natural logarithm function
exp2 base-2 exponentiation function
log2 base-2 logarithm function
sqrt square-root function
inversesqrt reciprocal of square-root function
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1347
Built-In Functions (Continued 2)
Common Functions
Function Description
abs absolute-value function
sign signum function
floor floor function
ceil ceiling function
fract fractional-part function
mod modulo function
min minimum of two values
max maximum of two values
clamp clamp value to specified range
mix affine combination of two values
step step function
smoothstep smooth step function
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1348
Built-In Functions (Continued 3)
Geometric Functions
Function Description
length length of vector
distance distance between two points
dot dot product
cross cross product
normalize get vector of unit length
faceforward get vector that points in same direction as ref-
erence vector
reflect get vector that points in direction of reflection
refract get vector that points in direction of refraction
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1349
Built-In Functions (Continued 4)
Matrix Functions
Function Description
matrixCompMult multiply matrices component-wise
Texture Lookup
Function Description
texture2D perform 2D texture lookup
textureCube perform cubemap texture lookup
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1350
Built-In Functions (Continued 5)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1351
The in and out Qualifiers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1352
The in and out Qualifiers (Continued)
example:
void calc(float x, int i, out float y, out int j) {
// at this point, y and j are undefined
y = ++x;
j = ++i;
}
void func() {
float a = 0.0;
int b = 0;
float c = 0.0;
int d = 0;
calc(a, b, c, d);
// a and b are unchanged by function call
// c is 1.0, d is 1
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1353
The uniform Qualifier
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1355
Interpolation Example
single triangle rendered with vertices having color attributes of red, green,
and blue, with provoking vertex being last vertex
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1356
Layout Qualifiers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1357
Configuration with Vertex and Fragment Shaders
Application Program
Shader Program
Uniform
Variables
Vertex Shader Fragment Shader
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1358
Various Configurations of Shaders
Vertex Fragment
Shader Primitive Shader
Assembly
and
Rasterization
Vertex and Fragment Shaders
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1359
Vertex Shaders
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1360
Vertex Shader Inputs and Outputs
instanced rendering
other inputs associated with vertex attributes from VAO/VBO
built-in output variables:
vec4 gl_Position: clip-space output position of current vertex
float gl_PointSize: pixel width/height of point being rasterized; only
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1361
Vertex Shader Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1362
Fragment Shaders
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1363
Fragment Shader Inputs and Outputs
gl_FragCoord.z
vec4 output variable for fragment color
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1364
Fragment Shader Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1365
Geometry Shaders
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1366
Geometry Shader Inputs
one input primitive per geometry shader invocation
type of input primitives specified by layout qualifier, which is one of:
points, lines, lines_adjacency, triangles,
triangles_adjacency
number of input vertices determined by input primitive type (e.g., three for
triangles)
per-vertex inputs available as members of elements in array gl_in:
vec4 gl_Position: vertex position
float gl_PointSize: pixel width/height of point being rasterized; only
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1367
Geometry Shader Outputs
type of output primitive generated specified by layout qualifier, which is
one of: points, line_strip, triangle_strip
can generate zero or more output primitives
maximum number of vertices that can be generated specified by
max_vertices layout qualifier
per-vertex outputs:
vec4 gl_Position: vertex position
float gl_PointSize: pixel width/height of point being rasterized; only
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1369
Using Shader Programs
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1371
Identifying Shader Variables in Application (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1372
Associating Data in VAO with Attribute Variable
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1373
Example: Associating Data in VAO with Attribute Variable
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1374
Accessing Uniform Variables from Application Program
application program needs to be able to access uniform variables in
shader
application can only write to uniform variables since data flows in one
direction only (i.e., from application to shader)
uniform variable identified by location
to modify uniform variable, must know its location
modify uniform variable via glUniform* (which identifies variable to
change by its location)
example:
Part of Shader
uniform float uTime;
Part of Application Program
GLuint program; // shader program ID
// ...
GLint loc = glGetUniformLocation(program, "uTime");
glUniform1f(loc, 1.5f);
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1375
Section 5.6.3
Shader Examples
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1376
Simple: Shader Example
vertex shader provided with two attributes per vertex (position and color)
want smooth interpolation of color across faces
rending output shown below for mesh consisting of single triangle
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1377
Simple: Vertex and Fragment Shaders
Vertex Shader
1 #version 330
2
3 in vec4 aPosition; // input vertex position attribute
4 in vec4 aColor; // input vertex color attribute
5
6 out vec4 vColor; // output vertex color (interpolated)
7
8 // uniform variable for modelview-projection
9 // matrix product
10 uniform mat4 uModelViewProjMatrix;
11
12 void main() {
13 vColor = aColor;
14 gl_Position = uModelViewProjMatrix * aPosition;
15 }
Fragment Shader
1 #version 330
2
3 in vec4 vColor; // input color (interpolated)
4
5 out vec4 fColor; // output fragment color
6
7 void main() {
8 fColor = vColor;
9 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1378
Mandelbrot: Shader Example
render triangles to cover entire drawing area and texture map Mandelbrot
set onto triangles using fragment shader
some examples of rendering results shown below
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1379
Mandelbrot: Background
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1380
Mandelbrot: Application Program
v3 = (−1, 1) v2 = (1, 1)
t3 = (− 12 , 12 ) t2 = ( 21 , 12 )
scale/2
center
scale/2
application program simply renders two triangles that cover full extent of
viewport ({vk } are positional coordinates; {tk } are texture coordinates)
texture coordinate region [− 21 , 21 ] × [− 12 , 12 ] corresponds to full viewport
square region in complex plane of width/height scale centered at point
center is mapped onto region [− 21 , 21 ] × [− 12 , 21 ] in texture coordinates
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1381
Mandelbrot: Vertex Shader
1 #version 330
2
3 in vec3 aPosition; // position vertex attribute
4 in vec3 aTexCoord; // texture-coordinate vertex attribute
5
6 out vec3 vTexCoord; // texture coordinate (interpolated)
7
8 void main() {
9 vTexCoord = aTexCoord;
10 gl_Position = vec4(aPosition, 1.0);
11 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1382
Mandelbrot: Fragment Shader
1 #version 330
2
3 in vec3 vTexCoord; // texture coordinates
4
5 out vec4 fColor; // vertex color
6
7 uniform vec2 center; // center of viewing region
8 uniform float scale; // width/height of viewing region
9 uniform int maxIters; // maximum iteration count
10
11 int mandelbrot(vec2 c) {
12 vec2 z = vec2(0.0, 0.0);
13 int i;
14 for (i = 0; i < maxIters; ++i) {
15 z = vec2(z.x * z.x - z.y * z.y + c.x, 2.0 * z.x * z.y + c.y);
16 if (length(z) > 2.0) {break;}
17 }
18 return i;
19 }
20
21 float lookup(float x, float c) {return c * mod(x, 1.0 / c);}
22
23 void main() {
24 int i = mandelbrot(vec2(scale * vTexCoord.x + center.x,
25 scale * vTexCoord.y + center.y));
26 float t = float(i) / maxIters;
27 fColor = vec4(lookup(t, 2.0), lookup(t, 4.0), lookup(t, 8.0), 1.0);
28 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1383
ShrinkFace: Shader Example
v1
v′1
c
v′2 v′0
v2 v0
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1385
ShrinkFace: Gap Filling
v1
v′1
c
v′2 v′0
v2 v0
gap can be filled with triangle strip with vertices: v2 , v′2 , v1 , v′1 , v0 , v′0 , v2 , v′2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1386
ShrinkFace: Vertex Shader
1 #version 330
2
3 in vec3 aPosition; // position vertex attribute
4 in vec3 aColor; // color vertex attribute
5
6 out vec3 vColor; // color (interpolated)
7
8 void main() {
9 gl_Position = vec4(aPosition, 1.0);
10 vColor = aColor;
11 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1387
ShrinkFace: Geometry Shader
1 #version 330
2
3 layout(triangles) in; // triangle primitives as input
4 in vec3 vColor[]; // input vertex colors
5
6 layout(triangle_strip, max_vertices=11) out;
7 // triangle strips as output; at most 11 vertices
8 out vec3 gColor; // output color (interpolated)
9
10 uniform mat4 uModelViewProjMatrix;
11 // modelview-projection matrix product
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1388
ShrinkFace: Geometry Shader (Continued)
13 void main() {
14 vec3 v[6];
15 for (int i = 0; i < 3; ++i) {v[i] = gl_in[i].gl_Position.xyz;}
16
17 // compute centroid of triangle
18 vec3 c = (v[0] + v[1] + v[2]) / 3.0;
19
20 // compute vertices of shrunk triangle and generate
21 // triangle strip consisting only of shrunk triangle
22 for (int i = 0; i < 3; ++i) {
23 v[i + 3] = c + 0.5 * (v[i] - c);
24 gl_Position = uModelViewProjMatrix * vec4(v[i + 3], 1.0);
25 gColor = vColor[i];
26 EmitVertex();
27 }
28 EndPrimitive();
29
30 // generate triangle strip to fill gap between triangles
31 // introduced by shrinking
32 const int lut[] = int[](2, 5, 1, 4, 0, 3, 2, 5);
33 for (int i = 0; i < 8; ++i) {
34 gl_Position = uModelViewProjMatrix * vec4(v[lut[i]], 1.0);
35 gColor = vec3(0.0, 0.0, 0.0);
36 EmitVertex();
37 }
38 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1389
ShrinkFace: Fragment Shader
1 #version 330
2
3 in vec3 gColor; // input color
4
5 out vec4 fColor; // output color
6
7 void main() {
8 fColor = vec4(gColor, 1.0);
9 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1390
Wireframe: Shader Example
1 1
2 p0 + 0p1 + 2 p2 0p0 + 21 p1 + 12 p2
1 1
≡ ( 2 , 0, 2 ) ≡ (0, 21 , 12 )
1 1 1
3 p0 + 3 p1 + 3 p2
≡ ( 13 , 31 , 31 )
1 1
p0 2 p0 + 2 p1 + 0p2 p1
1p0 + 0p1 + 0p2 ≡ ( 12 , 21 , 0) 0p0 + 1p1 + 0p2
≡ (1, 0, 0) ≡ (0, 1, 0)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1392
Wireframe: Vertex Shader
1 #version 330
2
3 in vec3 aPosition; // position vertex attribute
4 in vec3 aColor; // color vertex attribute
5
6 out vec3 vColor; // output color (interpolated)
7
8 uniform mat4 uModelViewProjMatrix;
9 // modelview-projection matrix product
10
11 void main() {
12 gl_Position = uModelViewProjMatrix * vec4(aPosition, 1.0);
13 vColor = aColor;
14 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1393
Wireframe: Geometry Shader
1 #version 330
2
3 layout(triangles) in; // triangles as input
4 in vec3 vColor[]; // vertex colors
5
6 layout(triangle_strip, max_vertices=3) out;
7 // triangle strips as output; at most 3 vertices
8 out vec3 gColor; // output color
9 noperspective out vec3 gBaryCoord;
10 // output barycentric coordinates (interpolated)
11
12 void main() {
13 const vec3 lut[3] = vec3[3](
14 vec3(1.0, 0.0, 0.0),
15 vec3(0.0, 1.0, 0.0),
16 vec3(0.0, 0.0, 1.0));
17 for (int i = 0; i < 3; ++i) {
18 gl_Position = gl_in[i].gl_Position;
19 gBaryCoord = lut[i];
20 gColor = vColor[i];
21 EmitVertex();
22 }
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1394
Wireframe: Fragment Shader
1 #version 330
2
3 in vec3 gColor; // input color
4 noperspective in vec3 gBaryCoord;
5 // input barycentric coordinates
6
7 out vec4 fColor; // output color
8
9 void main() {
10 const vec3 edgeColor = vec3(0.0, 0.0, 0.0);
11 const float edgeWidth = 1.0;
12 vec3 d = fwidth(gBaryCoord);
13 vec3 a3 = smoothstep(vec3(0.0), d * edgeWidth, gBaryCoord);
14 float v = min(min(a3.x, a3.y), a3.z);
15 fColor = vec4(mix(edgeColor, gColor, v), 1.0);
16 }
material properties:
ka : ambient reflection constant
vectors:
ℓ: unit vector vector in direction from point on surface to light source
Light Source
n
r
Eye
v θ θ −ℓ
Surface
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1397
Per-Vertex Lighting: Shader Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1398
Per-Vertex Lighting: Vertex Shader
1 #version 330
2
3 in vec3 aPosition; // position vertex attribute
4 in vec3 aNormal; // normal vertex attribute
5
6 out vec3 vColor; // output color (interpolated)
7
8 uniform mat4 uModelViewMatrix; // modelview matrix
9 uniform mat3 uNormalMatrix; // normal transformation matrix
10 uniform mat4 uModelViewProjMatrix;
11 // modelview-projection matrix product
12
13 struct LightSourceParams {
14 vec4 position; // position
15 vec3 ambient; // ambient component
16 vec3 diffuse; // diffuse component
17 vec3 specular; // specular component
18 };
19 uniform LightSourceParams uLight; // light parameters
20
21 struct MaterialParams {
22 vec3 ambient; // ambient reflectance
23 vec3 diffuse; // diffuse reflectance
24 vec3 specular; // specular reflectance
25 float shininess; // specular exponent
26 };
27 uniform MaterialParams uMaterial; // material parameters
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1399
Per-Vertex Lighting: Vertex Shader (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1400
Per-Vertex Lighting: Fragment Shader
1 #version 330
2
3 in vec3 vColor; // input color
4
5 out vec4 fColor; // output color
6
7 void main() {
8 fColor = vec4(vColor, 1.0);
9 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1401
Per-Fragment Lighting: Shader Example
1 #version 330
2
3 in vec3 aPosition; // position vertex attribute
4 in vec3 aNormal; // normal vertex attribute
5
6 out vec3 vPosition; // output position (interpolated)
7 out vec3 vNormal; // output normal (interpolated)
8
9 uniform mat4 uModelViewMatrix; // modelview matrix
10 uniform mat3 uNormalMatrix; // normal transformation matrix
11 uniform mat4 uModelViewProjMatrix;
12 // modelview-projection matrix product
13
14 void main() {
15 vNormal = normalize(uNormalMatrix * aNormal);
16 vPosition = vec3(uModelViewMatrix * vec4(aPosition, 1.0));
17 gl_Position = uModelViewProjMatrix * vec4(aPosition, 1.0);
18 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1403
Per-Fragment Lighting: Fragment Shader
1 #version 330
2
3 in vec3 vNormal; // input normal
4 in vec3 vPosition; // input position
5
6 out vec4 fColor; // output color
7
8 struct LightSourceParams {
9 vec4 position; // position
10 vec3 ambient; // ambient component
11 vec3 diffuse; // diffuse component
12 vec3 specular; // specular component
13 };
14 uniform LightSourceParams uLight; // light parameters
15
16 struct MaterialParams {
17 vec3 ambient; // ambient reflectance
18 vec3 diffuse; // diffuse reflectance
19 vec3 specular; // specular reflectance
20 float shininess; // specular exponent
21 };
22 uniform MaterialParams uMaterial; // material parameters
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1404
Per-Fragment Lighting: Fragment Shader (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1405
Section 5.6.4
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1406
OpenGL Example Program: simple_2d
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1407
OpenGL Example Program: simple_3d
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1408
OpenGL Example Program: cube
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1409
OpenGL/CGAL Example Program: wireframe
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1410
Section 5.6.5
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1411
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1412
References II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1413
Talks I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1414
Software
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1415
Section 5.7
Other Libraries
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1416
Numerical Libraries I
Eigen
C++ library for linear algebra
web site: http://eigen.tuxfamily.org
Lapack++
C++ library for high-performance linear-algebra computations
C++ wrapper for LAPACK and BLAS
web site: http://lapackpp.sourceforge.net
Armadillo
C++ library for linear algebra
web site: http://arma.sourceforge.net
GNU Scientific Library
C library for numerical analysis
web site: http://www.gnu.org/software/gsl
GNU Multiprecision Library
C library for arbitrary-precision arithmetic
web site: http://gmplib.org
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1417
Numerical Libraries II
Boost.uBLAS
C++ library for numerical computation
web site: http://www.boost.org/doc/libs/release/libs/numeric/
ublas
Boost.Rational
C++ rational number library
web site: www.boost.org/doc/libs/release/libs/rational
Boost.Interval
C++ interval arithmetic library
web site: www.boost.org/doc/libs/release/libs/numeric/
interval/doc/interval.htm
Boost.Math
C++ library
provides math constants, GCD, LCM, quaternions, and more
web site: http://www.boost.org/doc/libs/release/libs/math
Linear Algebra Package (LAPACK)
Fortran library for numerical computing
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1418
Numerical Libraries III
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1419
Part 6
Programming
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1420
Section 6.1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1421
Formatting, Naming, Documenting
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1422
Error Handling
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1423
Simplicity
Do not unnecessarily complicate code. Use the simplest solution that will
meet the needs of the problem at hand.
Do not impose bogus limitations. If a more general case can be handled
without complicating the code and this more general case is likely to be
helpful to handle, then handle this case.
Do not unnecessarily optimize code. Highly optimized code is often much
less readable. Also, highly optimized code is often more difficult to write
correctly (i.e., without bugs). Do not write grossly inefficient code that is
obviously going to cause performance problems, but do not optimize
things beyond avoiding gross inefficiencies that you know will cause
performance problems.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1424
Code Duplication
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1425
Miscellany
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1426
Miscellany
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1427
Testing: Preconditions and Postconditions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1428
Testing
The single most important thing when writing code is that it does the job it
was intended to do correctly. That is, there should not be any bugs.
Test your code. If you do not spend as much time testing your code as you
do writing it, you are likely not doing enough testing.
Tests should exercise as much of the code as possible (i.e., provide good
code coverage).
Design and structure your code so that it is easy to test. In other words,
testing should be considered during design.
Your code will have bugs. Design your code so that it will help you to
isolate bugs. Use assertions. Use preconditions and postconditions.
Design your code so that is modular and can be written and tested in
pieces. The first testing of the software should never be testing the entire
software as a whole.
Often in order to adequately test code, one has to write separate
specialized test code.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1429
Code Examples
subscripting operator for 1-D array class:
template <class T>
const T& Array_1<T>::operator[](int i) const {
// Precondition: index is in allowable range
assert(i >= 0 && i < data_.size());
return data_[i];
}
function taking pointer parameter:
int stringLength(const char* ptr) {
// Precondition: pointer is not null
assert(ptr);
// Code to compute and return string length.
// ...
}
function that modifies highly complicated data structure:
void modifyDataStructure(Type& dataStructure) {
// Precondition: data structure is in valid state
assert(isDataStructureValid(dataStructure));
// Complicated code to update data structure.
// ...
// Postcondition: data structure is in valid state
assert(isDataStructureValid(dataStructure));
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1430
Section 6.2
Algorithms
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1431
Software Performance
two most basic performance measures, which are often of most interest:
1 time complexity
2 space complexity
time complexity: amount of time required to execute code
space complexity: amount of memory needed for code execution
normally must consider both time and space complexities, since one type
of complexity can often be traded off for other
from practical standpoint, real-world time and memory usage are what
matter most (as opposed to some approximate theoretical measures of
code complexity)
need techniques that can provide guidance when designing software so
that more likely that later implementation (of design) will have acceptable
performance
many factors can potentially impact performance, including:
CPU instruction count
cache efficiency
degree of parallelism and concurrency
resource utilization (e.g., memory, disk, and network)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1432
Random-Access Machine (RAM) Model
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1435
Big-Theta (Θ) Notation
big-theta (Θ) notation: for function g, Θ(g) denotes set of all functions f
for which positive constants c1 , c2 , and n0 exist such that
0 ≤ c1 g(n) ≤ f (n) ≤ c2 g(n) for all n ≥ n0
functions in Θ(g) grow asymptotically at same rate as g (to within constant
factor)
effectively, f (n) is sandwiched between c1 g(n) and c2 g(n) for sufficiently
large n (i.e., n ≥ n0 )
used to provide (asymptotic) lower and upper bounds on function, each
to within constant factor (provides asymptotically tight bound)
if f ∈ Θ(g), then for sufficiently large n, f (n) equals g(n) to within
constant factor
examples:
f (n) = an2 + bn + c where a, b, c are constants and a > 0;
f (n)
c1 g(n)
n
n0
f ∈ Θ(g)
for n ≥ n0 , f (n) is lower bounded by c1 g(n) and upper bounded by
c2 g(n)
asymptotically, f grows at same rate as g to within constant factor
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1437
Big-Oh (O) Notation
big-oh (O) notation: for function g, O(g) denotes set of all functions f for
which positive constants c and n0 exist such that
0 ≤ f (n) ≤ cg(n) for all n ≥ n0
functions in O(g) grow asymptotically at rate at most that of g (to within
constant factor)
used to provide (asymptotic) upper bound on function to within constant
factor
if f ∈ O(g), then for sufficiently large n, f (n) is less than or equal to g(n)
to within constant factor
since Θ(g(n)) ⊂ O(g(n)), f (n) ∈ Θ(g(n)) implies f (n) ∈ O(g(n))
often used to bound worst-case running time of algorithm
examples:
f (n) = 3n2 + 2n + 1; f ∈ O(n2 ) and f ∈ O(n3 ) but f 6∈ O(n)
f (n) = 5n + 42; f ∈ O(n) and f ∈ O(n2 ) but f 6∈ O(1)
f (n) = ∑d i
i=0 ai n where {ai } are constants and ad > 0;
f ∈ O(nd ) and f ∈ O(nd+1 ) but f 6∈ O(nd−1 )
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1438
Big-Oh (O) Notation (Continued)
cg(n)
f (n)
n
n0
f ∈ O(g)
for n ≥ n0 , f (n) is upper bounded by cg(n)
asymptotically, f grows at rate no greater than that of g to within constant
factor
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1439
Big-Omega (Ω) Notation
big-omega (Ω) notation: for function g, Ω(g) denotes set of all functions
f for which positive constants c and n0 exist such that
0 ≤ cg(n) ≤ f (n) for all n ≥ n0
functions in Ω(g) grow asymptotically at rate at least that of g (to within
constant factor)
used to provide (asymptotic) lower bound on function to within constant
factor
if f ∈ Ω(g), then for sufficiently large n, f (n) is greater than or equal to
g(n) to within constant factor
since Θ(g(n)) ⊂ Ω(g(n)), f (n) ∈ Θ(g(n)) implies f (n) ∈ Ω(g(n))
examples:
f (n) = 5n3 + n; f ∈ Ω(n3 ) and f ∈ Ω(n2 ) but f 6∈ Ω(n4 )
f (n) = an2 + bn + c where a, b, c are constants and a > 0;
f (n)
cg(n)
n
n0
f ∈ Ω(g)
for n ≥ n0 , f (n) lower bounded by cg(n)
asymptotically, f grows at rate no less than that of g to within constant
factor
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1441
Small-Oh (o) Notation
small-oh (o) notation: for function g, o(g) denotes set of all functions f
such that, for any positive constant c, positive constant n0 exists such that
0 ≤ f (n) < cg(n) for all n ≥ n0
functions in o(g) grow asymptotically at strictly lesser rate than g (to
within constant factor)
used to provide upper bound on function that is not asymptotically tight
f ∈ o(g) implies that f (n) becomes insignificant relative to g(n) as n
f (n)
becomes arbitrarily large (i.e., limn→∞ g(n) = 0)
examples:
f (n) = 3n3 + 2n + 1; f ∈ o(n5 ) and f ∈ o(n4 ) but f 6∈ o(n3 )
f (n) = 2n2 ; f 6∈ o(n2 ) but f ∈ O(n2 )
f (n) = ∑d i
i=0 ai n where {ai } are constants and ad > 0;
f ∈ o(nd+1 ) and f ∈ o(nd+2 ) but f 6∈ o(nd ) and f 6∈ o(nd−1 )
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1442
Small-Omega (ω) Notation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1443
Asymptotic Notation in Equations and Inequalities
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1444
Properties of Θ, O, and Ω
sum of functions:
if f 1 ∈ Θ(g) and f 2 ∈ Θ(g), then f 1 + f 2 ∈ Θ(g)
multiplication by constant:
for all positive functions f and all positive constants a, a f ∈ Θ( f ),
a f ∈ O( f ), and a f ∈ Ω( f )
product of functions:
for all positive functions f1 , f2 , g1 , g2 , if f1 ∈ Θ(g1 ) and f2 ∈ Θ(g2 ), then
f1 f2 ∈ Θ(g1 g2 )
for all positive functions f1 , f2 , g1 , g2 , if f1 ∈ O(g1 ) and f2 ∈ O(g2 ), then
f1 f2 ∈ O(g1 g2 )
for all positive functions f1 , f2 , g1 , g2 , if f1 ∈ Ω(g1 ) and f2 ∈ Ω(g2 ), then
f1 f2 ∈ Ω(g1 g2 )
examples:
if f ∈ Θ(n), then n f (n) ∈ Θ(n2 )
if f and g are positive functions in Θ(1), then f + g ∈ Θ(1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1445
Additional Remarks
log2 n ∈ Θ(logb n) for all b > 1 (i.e., base of logarithm does not impact
asymptotic analysis)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1446
Remarks on Asymptotic Complexity
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1447
Some Common Complexities
Name Complexity
constant O(1)
logarithmic O(log n)
fractional power O(nc ), c ∈ (0, 1)
linear O(n)
log-linear O(n log n)
quadratic O(n2 )
cubic O(n3 )
exponential O(an )
factorial O(n!)
b n
double exponential O(a )
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1448
Recurrence Relations
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1449
Solving Recurrence Relations
no known general technique for solving recurrence relations
solving recurrence relations somewhat of an art
linear constant coefficient difference equations can be solved using z
transform
Master theorem can be used to solve some recurrence relations of form:
f (n) = g(n) + a f (n/b)
Akra-Bazzi theorem can be used to solve some recurrence relations of
form:
f (n) = g(n) + ∑L−1
i=0 ai f (bi n + hi (n))
need to be careful about non-integer sequence indices arising in
recurrence relations like:
T (n) = ∑L−1
i=0 ai T (n/bi ) + f (n)
preceding formula does not make sense if n/bi is not integer
in many cases, if this issue ignored, correct asymptotic bound still
obtained, although without being correctly justified
numerous software tools available for solving recurrence relations, such
as WolframAlpha and PURRS
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1450
Solutions for Some Common Recurrence Relations
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1451
Matrix Multiplication Algorithm: Time Complexity
consider algorithm for multiplying m × n matrix by n × p matrix:
1 template <class T, int m, int n, int p>
2 void multiply(const T (&a)[m][n], const T (&b)[n][p],
3 T (&c)[m][p]) {
4 for (int i = 0; i < m; ++i) {
5 for (int j = 0; j < p; ++j) {
6 T sum = T(0);
7 for (int k = 0; k < n; ++k) {
8 sum += a[i][k] * b[k][j];
9 }
10 c[i][j] = sum;
11 }
12 }
13 }
total time cost per line (assuming basic operations on T are O(1)):
Line Total Time Cost
4 c4,1 m + c4,2
5 m(c5,1 p + c5,2 )
6 mp(c6 )
7 mp(c7,1 n + c7,2 )
8 mpn(c8 )
10 mp(c10 )
asymptotic time complexity is a1 mnp + a2 mp + a3 m + a4 = Θ(mnp)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1452
Matrix Multiplication Algorithm: Space Complexity
again, consider algorithm for multiplying m × n matrix by n × p matrix:
1 template <class T, int m, int n, int p>
2 void multiply(const T (&a)[m][n], const T (&b)[n][p],
3 T (&c)[m][p]) {
4 for (int i = 0; i < m; ++i) {
5 for (int j = 0; j < p; ++j) {
6 T sum = T(0);
7 for (int k = 0; k < n; ++k) {
8 sum += a[i][k] * b[k][j];
9 }
10 c[i][j] = sum;
11 }
12 }
13 }
a, b, and c are references and each effectively incur memory cost of
pointer
m, n, and p are constant expressions are require no storage
assuming objects of type T require O(1) space, each of a, b, c, i, j, k,
and sum, require Θ(1) space
asymptotic space complexity is Θ(1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1453
Iterative Fibonacci Algorithm: Time Complexity
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1454
Iterative Fibonacci Algorithm: Space Complexity
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1455
Recursive Fibonacci Algorithm: Time Complexity
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1456
Recursive Fibonacci Algorithm: Space Complexity
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1457
Amdahl’s Law
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1458
Section 6.2.1
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1459
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1461
Section 6.3
Data Structures
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1462
Abstract Data Types (ADTs)
abstract data type (ADT) is model for data type where behavior
specified from point of view of user of type (i.e., with implementation
details hidden)
ADT specifies:
general nature of entity represented by type
set of allowable states/values that type can assume
set of operations that can be performed on type
any preconditions or postconditions for operations
often, ADT also provides complexity guarantees (e.g., time or space
complexity guarantees for various operations)
for example, (generic) integer type is ADT:
can assume integer values
provides basic arithmetic operations, relational operations, and so on
particular representation used for integers not specified by ADT
in contrast to ADT, concrete (i.e., non-abstract) data type provides very
specific details as to how type is implemented
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1463
Container ADTs
std::unordered_map, std::unordered_multimap
boost::intrusive::slist, boost::intrusive::list
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1465
Iterator ADTs
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1467
Container and Iterator Considerations (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1468
Section 6.3.1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1469
List ADT
list ADT is ADT that stores countable number of ordered values, where
same value may occur more than once
operations for list ADT include:
clear: remove all elements from list
is empty: test if list empty
size: query number of elements in list
insert: insert element in list
remove: remove element from list
operations for traversing elements in list (which are often provided via
iterator ADT) include:
successor: get next element in list
predecessor (optional): get previous element in list
examples of realizations of list ADT:
std::vector, std::forward_list, and std::list
boost::intrusive::slist and boost::intrusive::list
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1470
Array-Based Lists
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1471
Array-Based Lists: Diagram
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1472
Remarks on Array-Based Lists
advantages:
elements stored contiguously in memory (which is cache friendly)
no per-element storage overhead
can insert at end of list in amortized O(1) time
can remove at end of list in O(1) time
can access element in any position in O(1) time
(random-access) iterator has storage cost of one pointer
disadvantages:
cannot insert or remove at start or arbitrary position in O(1) time
if capacity of array exceeded, memory reallocation and copying required
if array can be reallocated, insert at end can only at best guarantee
amortized (not worst-case) O(1) time
if array reallocated, element references invalidated
useful when insertion and removal only performed at end of list and stable
references to elements not needed
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1473
Singly-Linked Lists
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1474
Singly-Linked Lists: Code
1 // list node
2 template <class T> struct Node {
3 Node* next_; // pointer to next node in list
4 T elem_; // element data
5 };
6
7 // list
8 template <class T> class List {
9 // ...
10 Node<T>* head_; // pointer to first node in list
11 std::size_t size_; // number of elements in list
12 };
13
14 // iterator
15 template <class T> class Iterator {
16 // ...
17 Node<T>* node_; // pointer to node with referenced element
18 Node<T>** head_;
19 // if before begin, pointer to list head pointer
20 // otherwise, null
21 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1475
Singly-Linked List: Diagram
Iterator
node_
head_
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1476
Remarks on Singly-Linked Lists
advantages:
can insert element after (but not before) particular position in O(1) time
can remove element at start of list in O(1) time
no capacity exceeded problem like with array
reduced memory cost relative to doubly-linked list as consequence of node
not tracking predecessor
element references are stable
can find successor in list in O(1) time
disadvantages:
element data not contiguous in memory
has per-element storage overhead (1 pointer for successor)
cannot insert element before particular position in O(1) time
cannot remove element at arbitrary position in O(1) time
cannot efficiently iterate backwards over elements in list
cannot find predecessor in list in O(1) time
(forward) iterator requires two pointers for state (due to need for
“before-begin” iterator)
typically useful when insertions and removals always performed at start of
list
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1477
Singly-Linked List With Header Node
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1478
Singly-Linked List With Header Node: Code
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1479
Singly-Linked List With Header Node: Diagram
Iterator
node_
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1480
Remarks on Singly-Linked List With Header Node
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1481
Doubly-Linked Lists
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1482
Doubly-Linked Lists: Code
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1483
Doubly-Linked List: Diagram
Iterator
node_
tail_
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1484
Remarks on Doubly-Linked Lists
advantages:
stable references to elements
can insert or remove at arbitrary position in O(1) time
no capacity-exceeded problem like in array case
can find successor and predecessor in O(1) time
can efficiently iterate both forwards and backwards over elements in list
disadvantages:
elements not stored contiguously in memory
per-element storage overhead (2 pointers)
relative to singly-linked list, has greater per-element storage overhead (1
additional pointer for predecessor)
iterator storage cost is more than single pointer (i.e., 2 pointers)
most useful for lists where insertion and removal can happen anywhere in
list
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1485
Doubly-Linked List With Sentinel Node
list has one dummy node called sentinel node and zero or more regular
(i.e., non-sentinel) nodes
list object itself has sentinel node as member
each regular node is associated with list element
sentinel node is not associated with any list element
each (regular and sentinel) node has pointer to its successor and
predecessor
if list not empty, successor of sentinel node is node corresponding to first
element in list; otherwise, successor is sentinel node itself
if list not empty, predecessor of sentinel node is node corresponding to
last element in list; otherwise, predecessor is sentinel node itself
thus, sentinel and regular nodes effectively form augmented list that is
circular
augmented list never empty, since always contains sentinel node
augmented list has has no beginning or end, since circular
using sentinel node eliminates many special cases for insertion and
removal, which leads to simpler and more efficient code
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1486
Doubly-Linked List With Sentinel Node: Code
1 // list node base class (which does not have element data)
2 struct Node_base {
3 Node_base* next_; // pointer to next node in list
4 Node_base* prev_; // pointer to previous node in list
5 };
6
7 // list node (which has element data)
8 template <class T> struct Node : public Node_base {
9 T elem_; // element data
10 };
11
12 // list
13 template <class T> class List {
14 // ...
15 Node_base node_; // sentinel node
16 };
17
18 // list iterator
19 template <class T> class Iterator {
20 // ...
21 Node_base* node_; // pointer to referenced node
22 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1487
Doubly-Linked List With Sentinel Node: Diagram
Iterator
node_
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1488
Remarks on Doubly-Linked Lists With Sentinel Node
advantages and disadvantages mostly similar to those of classic
doubly-linked list
effectively no memory cost for sentinel node over standard doubly-linked
list
sentinel node effectively makes list circular and always nonempty
sentinel node eliminates special cases caused by empty list and insertion
and removal at start and end of list (simplifying code)
use of sentinel node facilitates more efficient iterator type
in absence of sentinel node, null pointer would need to be used to indicate
end of list
this causes problems for efficient implementation of bidirectional iterator
(namely, consider predecessor operation for iterator that refers to end of
list)
use of sentinel node avoids this problem
(bidirectional) iterator can be implemented with single pointer as state
typically, doubly-linked list with sentinel node used to implement
std::list
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1489
Stack ADT
stack ADT is ADT for container where elements can only be inserted or
removed in last-in first-out (LIFO) order
can only insert and remove elements at top of stack
operations provided by stack ADT:
clear: remove all elements from stack
is empty: test if stack is empty
top: access element at top of stack (without removing)
push: add element to top of stack
pop: remove element from top of stack
stack overflow: attempting to perform push operation when insufficient
space available for element being added
stack underflow: attempting to perform pop operation when stack empty
example realizations of stack ADT:
std::stack
boost::lockfree::stack
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1490
Array Implementation of Stack
stack can be efficiently implemented using array
code example:
1 template <class T> class Stack {
2 // ...
3 T* start_; // pointer to start of element storage
4 T* end_; // pointer to end of element storage
5 T* ptr_; // pointer to next free slot on stack
6 };
stack empty if ptr_ equals start_
stack has reached capacity if ptr_ equals end_
push operation stores element at *ptr_ and then increments ptr_
pop operation decrements ptr_
top operation provides access to ptr_[-1]
due to possibility of exceeding array capacity, cannot guarantee each
push operation takes constant time; can only hope for amortized (not
worst-case) O(1) time
memory efficient: only per-element storage cost is element data itself
cache-efficient: element data is contiguous in memory
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1491
Array Implementation of Stack: Diagram
Stack Array of T
x0
start_
x1
end_ ..
.
ptr_ xn−1
..
.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1492
Remarks on Array Implementation of Stack
advantages:
elements stored contiguously in memory
no per-element storage overhead
disadvantages:
if capacity of array exceeded, must reallocate and copy
if array grown, can only guarantee amortized (not worst-case) O(1) time for
push
if array reallocated, elements references are invalidated
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1493
Node-Based Implementation of Stack
only need list to be singly linked (as opposed to doubly linked), since all
insertions and removals performed at start of list (i.e., top of stack)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1494
Node-Based Implementation of Stack: Diagram
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1495
Remarks on Node-Based Implementation of Stack
advantages:
no capacity-exceeded problem as in array case
can perform push operation in O(1) time in worst case
element references are stable
disadvantages:
element data not contiguous in memory
has per-element storage overhead (i.e., 1 pointer for successor)
relative to array-based implementation, requires more space
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1496
Queue ADT
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1497
Array Implementation of Queue
array implementation of bounded queue
code example:
1 // bounded queue
2 template <class T> Queue {
3 // ...
4 T* start_; // start of array for queue elements
5 T* end_; // end of array for queue elements
6 T* head_; // pointer to element at front of queue
7 T* tail_; // pointer to back of queue
8 std::size_t size_; // number of entries in queue
9 };
array used in circular fashion
queue is empty if size_ is zero
queue is full if size_ equals max_size
if queue not full, enqueue operation places element at tail_ and then
increments tail_ with wraparound and increments size_
if queue not empty, dequeue operation increments head_ with
wraparound and decrements size_
front operation provides access to *head_
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1498
Array Implementation of Queue: Diagram
Queue Array of T
start_
end_ ..
.
head_
x0
tail_
x1
size_ n ..
.
xn−1
..
.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1499
Remarks on Array Implementation of Queue
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1500
Array of Arrays Implementation of Queue
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1501
Array of Arrays Implementation of Queue: Diagram
Queue Block
map_
size_ 6 x0
x1
cur_
first_ Map
start_ Block
last_ 0 x2
0 x3
block_
x4
x5
cur_
0
first_
finish_ Block
last_
x6
block_ x7
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1502
Remarks on Array of Arrays Implementation of Queue
advantages:
elements never change their location so pointers and references to
elements are stable
disadvantages:
although each individual block holding element data is contiguous, blocks
not contiguous
although elements are never relocated by insertions and removals, iterators
can be invalidated
similar data structure used in some implementations of std::deque
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1503
Node-Based Implementation of Queue
Iterator
node_
tail_
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1505
Remarks on Node-Based Implementation of Queue
advantages:
enqueue and dequeue operations can be performed in O(1) time
stable element references
disadvantages:
elements not stored contiguously in memory
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1506
Section 6.3.2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1507
Trees
tree is non-linear hierarchical data type
tree consists of zero or more nodes
except root, each node has parent
each node has zero or more children
tree containing no nodes is empty
node q said to be parent of node n if n is child of q
root node: node in tree with no parent
node q said to be sibling of node n if q and n have same parent
tree said to be ordered if linear ordering of children of each node
example:
A A is root node
B is child of A
B C D
A is parent of B
E F G H C and D are siblings of B
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1508
Tree Terminology
example:
A A, B, F is path of length 2
A and B are proper ancestors of E
B C D
E and F are proper descendants of B
E F G H B is ancestor and descendant of B
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1509
Tree Terminology (Continued 1)
subtree rooted at node n is tree that consists of n and all of its
descendants (e.g., subtree of root is entire tree)
degree of node is number of its children
degree of tree is maximum node degree taken over all nodes in tree
internal node is node that has at least one child
external node (also called leaf node) is node that does not have any
children
example:
tree consisting of nodes B, E , and F
A is subtree associated with node B
degree of node B is 2
B C D
degree of tree is 3
example:
A
depths of nodes C and E are 1 and 2,
respectively
example:
A
weights of nodes B and C are 2 and
B C D 0, respectively
weight of tree is 5
E F G H
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1512
Tree Traversal
example:
A
preorder traversal visits nodes in
order: A, B, E , F , C, D, G, H
B C D
postorder traversal visits nodes in
order: E , F , B, C, G, H , D, A
E F G H
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1513
Applications of Trees
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1514
Tree ADT
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1515
Node-Based Tree Implementation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1516
Node-Based Tree Implementation: Diagram
Tree Node
root_ parent_ 0
size_ 6 child_
sibling_ 0
elem_
Node Node
parent_ parent_
child_ 0 child_ 0
sibling_ sibling_ 0
elem_ elem_
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1517
Binary Trees
example:
root node is A
A
left child of A is B
B C right child of A is C
D E F
left subtree of A is tree consisting of nodes B, D, and E
right subtree of A is tree consisting of nodes C and F
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1518
Perfect and Complete Trees
binary tree said to be perfect (or full) if each internal node has exactly
two children (which results in all leaves being at same level)
binary tree said to be complete if perfect except possibly for deepest level
which must be filled from left to right
perfect implies complete
Perfect Complete
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1519
Balanced Binary Trees
binary tree said to be perfectly balanced all leaf nodes have same depth
binary tree said to be strictly balanced if, for any two leaf nodes,
difference in their depth is at most one
binary tree said to be height balanced if height of left and right subtrees
of each (interior) node differ by at most one
perfectly balanced implies strictly balanced (but converse does not hold)
strictly balanced implies height balanced (but converse does not hold)
Perfectly Balanced
Strictly Balanced Height Balanced
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1520
Binary Tree Traversal
preorder traversal: visit node, then left subtree, then right subtree
postorder traversal: visit left subtree, then right subtree, then node
level-order traversal: visit nodes from left to right within level from top
downwards
one additional traversal order for binary trees: in order
in-order traversal: visit left subtree, then node, then right subtree
example:
A preorder traversal order: A, B, D, E , C, F
postorder traversal order: D, E , B, F , C, A
B C
inorder traversal order: D, B, E , A, C, F
D E F
level order traversal order: A, B, C, D, E , F
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1521
Binary Tree ADT
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1522
Binary Tree ADT (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1523
Node-Based Binary Tree
root_ parent_ 0
size_ 5 left_
right_
elem_
Node Node
parent_ parent_
left_ left_ 0
right_ right_ 0
elem_ elem_
Node Node
parent_ parent_
left_ 0 left_ 0
right_ 0 right_ 0
elem_ elem_
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1525
Remarks on Node-Based Binary Tree
advantages:
can handle case of tree that is not complete without gross memory
inefficiency
can provide stable element references
disadvantages:
has per-element storage overhead (3 pointers: 1 for parent, 1 for first child,
and 1 for second child or next sibling)
element data not contiguous
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1526
Array-Based Binary Tree
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1527
Array-Based Binary Tree: Diagram
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1528
Remarks on Array-Based Binary Tree
advantages:
memory efficient: no per-element storage overhead (i.e., no memory cost
for representing connectivity of nodes in tree)
cache efficient: element data stored contiguously in memory
disadvantages:
can only handle complete trees
although could generalize this approach to handle non-complete tree,
would be grossly inefficient in terms of memory usage
if array capacity exceeded, costly reallocation and copy required
if array reallocation occurs, cannot provide stable references to elements
array implementation should be preferred for complete trees (unless
inability to guarantee stable element references is problematic)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1529
Binary Search Trees
binary tree is said to have binary search tree property if, for each node
node n with key k, following holds:
every key in left subtree of n is less than or equal to k; and
every key in right subtree of n is greater than or equal to k
20 60
10 30 50 70
5 25 35 45 80
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1530
Heaps
tree said to have heap property if, for each node n in tree, following
holds:
key of n is greater than or equal to key of each descendant of n
70 60
55 45 35 42
10 20 30 15 16
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1531
Section 6.3.3
Hash Tables
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1532
Basic Idea Behind Hash Tables
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1533
Hash Tables
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1534
Hash Table Example
Index Slot
0000
0001 0019910001
0002 5919870002
0003
..
.
9997 1212009997
9998
9999 1122339999
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1535
Hash Functions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1536
Remarks on Hash-Code Maps
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1537
Remarks on Compression Maps
a mod m 6= 0
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1538
Collision Resolution by Chaining
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1539
Collision Resolution by Open Addressing
with open addressing, only one element allowed to be stored per slot in
table so in case of collision alternate choice must be made for slot to store
element
sequence of indices to consider (in order) when inserting (or searching
for) element with given key called probe sequence
examine table at each position in probe sequence until slot for element is
found (e.g., empty slot for insertion)
for each possible key k, probe sequence should be permutation of
{0, 1, . . . , m − 1} so that all slots are reachable
many possible choices for probe sequence (e.g., linear, quadratic, double
hashing, and random hashing)
load factor α must satisfy α ≤ 1 (since only one element stored per slot)
uniform hashing: probe sequence of each key equally likely to be any of
m! permutations of {0, 1, . . . , m − 1}
1
number of probes in unsuccessful search is at most 1−α , assuming
uniform hashing
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1540
Linear Probing
with linear probing, probe sequence starts at hash value of key and then
proceeds as necessary sequentially, wrapping around to beginning of
table when end of table reached
ith value in probe sequence for key k given by h(k, i) = (h′ (k) + i) mod m,
where h′ is hash function
suffers from primary clustering, where colliding elements clump together
causing future collisions to generate longer sequence of probes
expected
number
of probes for insertion or unsuccessful search is
1 1
2 1 + (1−α) 2
1 1
expected number of probes for successful search is 2 1+ 1−α
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1541
Linear Probing Example
0 1 2 3 4 5 6 7 8 9 10 11 12
15 18
k h(k)
0 1 2 3 4 5 6 7 8 9 10 11 12
18 5
15 18 23
15 2
23 10
0 1 2 3 4 5 6 7 8 9 10 11 12
31 5
15 18 31 23
44 5
9 9
0 1 2 3 4 5 6 7 8 9 10 11 12
15 18 31 44 23
0 1 2 3 4 5 6 7 8 9 10 11 12
15 18 31 44 9 23
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1542
Quadratic Probing
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1543
Quadratic Probing Example
0 1 2 3 4 5 6 7 8 9 10 11 12
15 18
0 1 2 3 4 5 6 7 8 9 10 11 12 k h(k)
15 18 23 18 5
15 2
0 1 2 3 4 5 6 7 8 9 10 11 12 23 10
15 18 31 23 31 5
44 5
0 1 2 3 4 5 6 7 8 9 10 11 12 9 9
15 18 31 44 23
0 1 2 3 4 5 6 7 8 9 10 11 12
9 15 18 31 44 23
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1544
Double Hashing
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1545
Double Hashing Example
integer key; hash function h(k) = k mod 13; secondary hash function
d(k) = 7 − k mod 7
insert 18, 15, 23, 31, 44, and 9 (in order):
0 1 2 3 4 5 6 7 8 9 10 11 12
18
0 1 2 3 4 5 6 7 8 9 10 11 12
15 18
0 1 2 3 4 5 6 7 8 9 10 11 12
k h(k) d(k)
15 18 23
18 5 3
15 2 6
0 1 2 3 4 5 6 7 8 9 10 11 12 23 10 5
15 18 31 23 31 5 4
44 5 5
0 1 2 3 4 5 6 7 8 9 10 11 12 9 9 5
15 18 44 31 23
0 1 2 3 4 5 6 7 8 9 10 11 12
9 15 18 44 31 23
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1546
Random Hashing
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1547
Open Addressing: Insertion, Removal, and Search
if keep adding elements to hash table, eventually size of table will need to
be increased, due to loading factor becoming too large (for good
performance or correct behavior)
rehashing: rebuilding hash table with different number of slots
typical threshold for load factor α for rehashing:
1 for chaining
1
2 for open addressing
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1549
Some Applications of Hash Tables
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1550
Section 6.3.4
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1551
Set and Multiset ADTs
boost::intrusive::unordered_multiset
boost::intrusive::set and boost::intrusive::multiset
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1552
Map and Multimap ADTs
map (or associative array) ADT is container that stores pairs each
consisting of key and value, where keys are unique
each element in map consists of key and value
operations provided by map ADT include:
clear: remove all elements from map
is empty: test if map is empty
size: query number of elements in map
insert: insert element in map
remove: remove element from map
find: locate element in map if present based on its key
multimap ADT similar to map ADT except that restriction that keys must
be unique is dropped
example realizations of map/multimap ADT:
std::map and std::multimap
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1553
Remarks on Implementation of Sets and Maps
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1554
Red-Black Trees
some C++ standard library implementations use red-black trees for types
that provide binary search tree functionality (e.g., std::set and
std::map)
example realizations of red-black trees:
boost::intrusive::rbtree, boost::intrusive::set, and
boost::intrusive::multiset
example of red-black tree (where red nodes are shaded gray):
40
20 60
10 30 50 70
5 45 55 65 75
42 48
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1556
AVL Trees
since AVL trees more rigidly balanced than red-black trees, search
operations typically faster in AVL tree
insertion and removal operations typically slower in AVL tree than in
red-black tree, due to more work being required for tree re-balancing
example realizations of AVL trees:
boost::intrusive::avltree, boost::intrusive::avl_set, and
boost::intrusive::avl_multiset
example of AVL tree:
40
(+1)
20 60
(−1) (+1)
10 30 50 70
(−1) (0) (−1) (−1)
5 45 65 75
(0) (0) (0) (0)
62 66
(0) (0)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1558
Treaps
boost::intrusive::treap_multiset
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1559
Splay Trees
splay tree is self-adjusting binary search tree with property that searches
for more frequently accessed elements can be performed more quickly
splay tree keeps more recently accessed elements closer to root
caching effect comes at cost of tree rebalancing being required each time
search is performed
significant disadvantage of splay tree is that height of tree can become
linear in number of elements
in worst case, insertion, removal, and search operations take amortized
O(log n) time
example realizations of splay trees:
boost::intrusive::splay_tree, boost::intrusive::splay_set,
and boost::intrusive::splay_multiset
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1560
Scapegoat Trees
boost::intrusive::sg_multiset
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1561
Section 6.3.5
Priority Queues
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1562
Priority Queue ADT
priority queue ADT is ADT similar to queue except that each element on
queue also has corresponding priority
element at front of queue is always element with highest priority
operations provided by priority queue ADT include:
front: access element at front of queue (i.e., element with highest priority)
insert: insert element in queue with specified priority
remove: remove element from front of queue (i.e.. element with highest
priority)
update priority (optional): update priority of element in queue
if priority queue has stability property, elements with equal priority will
be removed in FIFO order
examples of realization of priority queue ADT:
std::priority_queue,
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1563
Remarks on Priority Queue Implementations
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1564
Section 6.3.6
Graphs
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1565
Graphs
B C D B C D
E E
Undirected Directed
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1566
Graph ADTs
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1567
Adjacency Matrix Implementation of Graph
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1568
Adjacency List Implementation of Graph
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1569
Section 6.3.7
Intrusive Containers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1570
Intrusive Containers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1571
Shortcomings of Non-Intrusive Containers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1572
Advantages of Intrusive Containers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1573
Disadvantages of Intrusive Containers
in order to use type with intrusive container, must change definition of type
each type stored in intrusive container needs additional memory to hold
information for container
intrusive containers unavoidably expose some implementation details of
container to user
since some implementation details are exposed, easier to break invariants
of container; for example:
changing key of element in map
corrupting pointers used to link nodes in container
user must assume responsibility for memory management (since
container does not)
user must manage lifetime of objects placed in container independent
from lifetime of container itself, which can be error prone:
when destroying container before object, must be careful to avoid resource
leaks
destroying object while in container, likely to be disasterous since container
uses part of object to implement container
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1574
Disadvantages of Intrusive Containers (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1575
Intrusive Doubly-Linked List
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1576
Intrusive Doubly-Linked List: Code
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1577
Intrusive Doubly-Linked List: Code (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1578
Intrusive Doubly-Linked List: Diagram
.. .. ..
. . .
Iterator
node_
tail_
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1579
Remarks on Intrusive Doubly-Linked List
node pointer and value pointer are equivalent (i.e., pointers to next and
previous nodes have type T*)
storage cost of iterator is two pointers (but one pointer would be more
desirable)
iterator state requires pointer to list tail pointer in order to handle case of
decrementing end iterator (which has null node pointer)
implementation does not require any non-portable constructs
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1580
Intrusive Doubly-Linked List With Sentinel Node
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1581
Intrusive Doubly-Linked List With Sentinel Node: Code
1 // list node class
2 struct list_hook {
3 // ...
4 list_hook* next_; // pointer to next node in list
5 list_hook* prev_; // pointer to previous node in list
6 };
7
8 // list traits class (no data members)
9 template <class T, list_hook<T> T::* P> class list_traits {
10 // functions for mapping between object and node pointers
11 };
12
13 // list iterator class (C determines if const_iterator)
14 template <class T, list_hook<T> T::* P, bool C> class list_iterator :
15 list_traits<T, P>{
16 // ...
17 list_hook* node_; // pointer to node of referenced element
18 };
19
20 // list
21 template <class T, list_hook<T> T::* P> class list :
22 list_traits<T, P> {
23 // ...
24 list_hook node_; // sentinel node
25 std::size_t size_; // number of elements in list
26 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1582
Intrusive Doubly-Linked List With Sentinel Node: Code (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1583
Intrusive Doubly-Linked List With Sentinel Node: Diagram
Iterator
node_
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1584
Remarks on Intrusive Doubly-Linked List With Sentinel Node
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1585
Examples of Intrusive Containers
multiset/multimap)
boost::intrusive_ptr (intrusive reference-counted smart pointer)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1586
Section 6.3.8
Miscellany
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1587
Memory Management for Containers
for reasons of efficiency or functionality (or even correctness), often
necessary to:
separate memory allocation from construction
separate memory deallocation from destruction
operator new can be used to perform only memory allocation (without
construction)
placement new can be used to perform only construction (without memory
allocation)
operator delete can be used to perform only memory deallocation (without
destruction)
direct invocation of destructor can be used to perform only destruction
(without memory deallocation)
allocator type provides interface that decouples allocation/deallocation
and construction/destruction
numerous convenience functions provided by standard library for dealing
with uninitialized storage
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1588
Section 6.3.9
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1589
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1590
References II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1591
Section 6.4
Finite-Precision Arithmetic
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1592
Code Example
What do each of the following functions output when executed?
void func1() {
double x = 0.1;
double y = 0.3;
double z = 0.4;
if (x + y == z) {
std::cout << "true\n";
} else {
std::cout << "false\n";
}
}
void func2() {
double x = 1e50;
double y = -1e50;
double z = 1.0;
if (x + y + z == z + y + x) {
std::cout << "true\n";
} else {
std::cout << "false\n";
}
}
void func3() {
for (double x = 0.0; x != 1.0; x += 0.1) {
std::cout << "hello\n";
}
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1593
Number Representations Using Different Radixes
Note: All numbers are base 10, unless explicitly indicated otherwise.
What is the representation of 31 in base 3?
1
3 = 0.3 = 0.13
1
What is the representation of 10 in base 2?
1
10 = 0.1 = 0.000112
A number may have a representation with a finite number of non-zero
digits in one particular number base but not in another.
Therefore, when a value must be represented with a limited number of
significant digits, the number base matters (i.e., affects the approximation
error).
1
For example, in base 2, 10 cannot be represented exactly using only a
finite number of significant digits.
0.000112 = 0.09375
0.0001100112 = 0.099609375
...
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1594
Finite-Precision Number Representations
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1595
Fixed-Point Number Representations
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1597
Floating-Point Number Representations (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1598
IEEE 754 Standard (IEEE Std. 754-1985)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1600
IEEE 754 Basic Formats
always represent number in normalized form whenever possible; in such
cases, b0 = 1 and b0 need not be stored explicitly as part of significand
bit patterns with reserved exponent values (i.e., exponent values that lie
outside the range Emin ≤ E ≤ Emax ) used to represent ±0, ±∞,
denormalized numbers, and NaNs
each of (basic) formats consist of three fields:
a sign bit, s
a biased exponent, e = E+ bias
a fraction, f = .b1 b2 · · · b p−1
single format:
1 8 23
s e f
MSB LSB MSB LSB
double format:
1 11 52
s e f
MSB LSB MSB LSB
summary of encodings:
Case Exponent Fraction Value
Normal Emin ≤ E ≤ Emax — (−1)s 2E (1 + f )
Denormal E = Emin − 1 f 6= 0 (−1)s 2Emin f
Zero E = Emin − 1 f =0 (−1)s 0
Infinity E = Emax + 1 f =0 (−1)s ∞
NaN E = Emax + 1 f 6= 0 NaN
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1602
IEEE 754 Encoding Examples
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1603
Finite-Precision Arithmetic
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1604
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1605
Talks I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1606
Section 6.5
Interval Arithmetic
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1607
Interval Arithmetic
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1608
Applications of Interval Arithmetic
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1609
Real Interval Arithmetic
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1610
Addition and Subtraction
addition:
A + B = [a1 , a2 ] + [b1 , b2 ] = [a1 + b1 , a2 + b2 ]
negation:
−B = −[b1 , b2 ] = [−b2 , −b1 ]
formula for negation follows from fact that:
x ≥ b1 ⇒ −x ≤ −b1 and
x ≤ b2 ⇒ −x ≥ −b2
subtraction:
A − B = [a1 , a2 ] − [b1 , b2 ] = [a1 − b2 , a2 − b1 ]
formula for subtraction follows from combining addition and negation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1611
Multiplication and Division
multiplication:
A · B = [a1 , a2 ] · [b1 , b2 ] =[min{a1 b1 , a1 b2 , a2 b1 , a2 b2 },
max{a1 b1 , a1 b2 , a2 b1 , a2 b2 }]
(e.g., [a1 , a2 ] · [b1 , b2 ] = [a1 b1 , a2 b2 ] if 0 ≤ a1 ≤ a2 and 0 ≤ b1 ≤ b2 )
reciprocal (assuming division by interval containing 0 not allowed):
1/B = 1/[b1 , b2 ] = [1/b2 , 1/b1 ]
formula for reciprocal follows from fact that, since 0 6∈ [b1 , b2 ],
x ∈ [b1 , b2 ], b1 , b2 all have same sign (implying b1 x > 0 and b2 x > 0) and
consequently:
x ≥ b1 ⇒ x ≥
b1
b1 x b1 x ⇒ 1/b1 ≥ 1/x ⇒ 1/x ≤ 1/b1
b2
x ≤ b2 ⇒ bx2 x ≤ ⇒ 1/b2 ≤ 1/x ⇒ 1/x ≥ 1/b2
b2 x
division (assuming division by interval containing 0 not allowed):
A/B = [a1 , a2 ]/[b1 , b2 ] =[min{a1 /b1 , a1 /b2 , a2 /b1 , a2 /b2 },
max{a1 /b1 , a1 /b2 , a2 /b1 , a2 /b2 }]
formula for division follows from fact that division is simply multiplication by
reciprocal
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1612
Allowing Division By Interval Containing Zero
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1613
Allowing Division By Interval Containing Zero (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1614
Floating-Point Interval Arithmetic
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1615
Floating-Point Interval Arithmetic (Continued)
must ensure that rounding does not cause interval to no longer bracket
result that would be obtained by (exact) real interval arithmetic
need to select shortest interval that contains result that would be obtained
from (exact) real interval arithmetic
lower bound of result must be computed with rounding downwards
upper bound of result must be computed with rounding upwards
using rounding in this way ensures that resulting interval will bracket
idealized (exact real) interval
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1616
Floating-Point Interval Arithmetic Operations
+
A + B = [a1 , a2 ] + [b1 , b2 ] = [a1 b1 , a2 + b2 ]
−
A − B = [a1 , a2 ] − [b1 , b2 ] = [a1 b2 , a2 − b1 ]
A · B = [a1 , a2 ] · [b1 , b2 ]
b2 ≤ 0 b1 < 0 < b2 b1 ≥ 0
a2 ≤ 0 [a2 ∗ b2 , a1 ∗ b1 ] [a1 ∗ b2 , a1 ∗ b1 ] [a1 ∗ b2 , a2 ∗ b1 ]
a1 < 0 < a2 [a2 ∗ b1 , a1 ∗ b1 ] [min{a1 ∗ b2 , a2 ∗ b1 }, [a1 ∗ b2 , a2 ∗ b2 ]
max{a1 ∗ b1 , a2 ∗ b2 ]}
a1 ≥ 0 [a2 ∗ b1 , a1 ∗ b2 ] [a2 ∗ b1 , a2 ∗ b2 ] [a1 ∗ b1 , a2 ∗ b2 ]
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1618
Addition and Subtraction
A+B
(−∞, b2 ] [b1 , b2 ] [b1 , +∞) (−∞, +∞)
(−∞, a2 ] (−∞, a2 + b2 ] (−∞, a2 + b2 ] (−∞, +∞) (−∞, +∞)
+ +
[a1 , a2 ] (−∞, a2 + b2 ] [a1 b1 , a2 + b2 ] [a1 b1 , +∞) (−∞, +∞)
+ +
[a1 , +∞) (−∞, +∞) [a1 b1 , +∞) [a1 b1 , +∞) (−∞, +∞)
(−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞)
A−B
(−∞, b2 ] [b1 , b2 ] [b1 , +∞) (−∞, +∞)
(−∞, a2 ] (−∞, +∞) (−∞, a2 − b1 ] (−∞, a2 − b1 ] (−∞, +∞)
− −
[a1 , a2 ] [a1 b2 , +∞) [a1 b2 , a2 − b1 ] (−∞, a2 − b1 ] (−∞, +∞)
− −
[a1 , +∞) [a1 b2 , +∞) [a1 b2 , +∞) (−∞, +∞) (−∞, +∞)
(−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1619
Multiplication
A·B
[b1 , b2 ] [b1 , b2 ] [b1 , b2 ]
[0, 0]
b2 ≤ 0 b1 < 0 < b2 b1 ≥ 0
[a1 , a2 ]
[a2 ∗ b2 , a1 ∗ b1 ] [a1 ∗ b2 , a1 ∗ b1 ] [a1 ∗ b2 , a2 ∗ b1 ] [0, 0]
a2 ≤ 0
[a1 , a2 ] [min{a1 ∗ b2 , a2 ∗ b1 },
[a2 ∗
b1 , a1 ∗ b1 ] [a1 ∗ b2 , a2 ∗ b2 ] [0, 0]
a1 < 0 < a2 max{a1 ∗ b1 , a2 ∗ b2 }]
[a1 , a2 ]
[a2 ∗ b1 , a1 ∗ b2 ] [a2 ∗ b1 , a2 ∗ b2 ] [a1 ∗ b1 , a2 ∗ b2 ] [0, 0]
a2 ≥ 0
[0, 0] [0, 0] [0, 0] [0, 0] [0, 0]
(−∞, a2 ]
[a2 ∗ b2 , +∞) (−∞, +∞) (−∞, a2 ∗ b1 ] [0, 0]
a2 ≤ 0
(−∞, a2 ]
[a2 ∗ b1 , +∞) (−∞, +∞) (−∞, a2 ∗ b2 ] [0, 0]
a2 ≥ 0
[a1 , +∞)
(−∞, a1 ∗ b1 ] (−∞, +∞) [a1 ∗ b2 , +∞) [0, 0]
a1 ≤ 0
[a1 , +∞)
(−∞, a1 ∗ b2 ] (−∞, +∞) [a1 ∗ b1 , +∞) [0, 0]
a1 ≥ 0
(−∞, ∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) [0, 0]
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1620
Multiplication (Continued)
A·B
(−∞, b2 ] (−∞, b2 ] [b1 , +∞) [b1 , +∞)
(−∞, +∞)
b2 ≤ 0 b2 ≥ 0 b1 ≤ 0 b1 ≥ 0
[a1 , a2 ]
[a2 ∗ b2 , +∞) [a1 ∗ b2 , +∞) (−∞, a1 ∗ b1 ] (−∞, a2 ∗ b1 ] (−∞, +∞)
a2 ≤ 0
[a1 , a2 ]
(−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞)
a1 < 0 < a2
[a1 , a2 ]
(−∞, a1 ∗ b2 ] (−∞, a2 ∗ b2 ] [a2 ∗ b1 , +∞) [a1 ∗ b1 , +∞) (−∞, +∞)
a2 ≥ 0
[0, 0] [0, 0] [0, 0] [0, 0] [0, 0] [0, 0]
(−∞, a2 ]
[a2 ∗ b2 , +∞) (−∞, +∞) (−∞, +∞) (−∞, a2 ∗ b1 ] (−∞, +∞)
a2 ≤ 0
(−∞, a2 ]
(−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞)
a2 ≥ 0
[a1 , +∞)
(−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞)
a1 ≤ 0
[a1 , +∞)
(−∞, a1 ∗ b2 ] (−∞, +∞) (−∞, +∞) [a1 ∗ b1 , +∞) (−∞, +∞)
a1 ≥ 0
(−∞, ∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1621
Division
A/B, 0 6∈ B
[b1 , b2 ] [b1 , b2 ] (−∞, b2 ] [b1 , +∞)
b2 < 0 b1 > 0 b2 < 0 b1 > 0
[a1 , a2 ] / / /
[a2 b1 , a1 /
b2 ] [a2 b1 , a2 /
b2 ] [0, a1 /
b2 ] [a1 b1 , 0]
a2 ≤ 0
[a1 , a2 ] / / / /
[a2 b2 , a1 /
b2 ] [a1 b1 , a2 /
b1 ] [a2 b2 , a1 /
b2 ] [a1 b1 , a2 /
b1 ]
a1 < 0 < a2
[a1 , a2 ] / / /
[a2 b2 , a1 /
b1 ] [a1 b2 , a2 /
b1 ] [a2 b2 , 0] [0, a2 /
b1 ]
a1 ≥ 0
[0, 0] [0, 0] [0, 0] [0, 0] [0, 0]
(−∞, a2 ] /
[a2 b1 , +∞) (−∞, a2 /
b2 ] [0, +∞) (−∞, 0]
a2 ≤ 0
(−∞, a2 ] / /
[a2 b2 , +∞) (−∞, a2 /
b1 ] [a2 b2 , +∞) (−∞, a2 /
b1 ]
a2 ≥ 0
[a1 , +∞) / /
(−∞, a1 /
b2 ] [a1 b1 , +∞) (−∞, a1 /
b2 ] [a1 b1 , +∞)
a1 ≤ 0
[a1 , +∞) /
(−∞, a1 /
b1 ] (a1 b2 , +∞) (−∞, 0] [0, +∞)
a1 ≥ 0
(−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1622
Division (Continued)
A/B, 0 ∈ B
[b1 , b2 ] [b1 , b2 ] (−∞, b2 ] [b1 , +∞)
[0, 0] (−∞, +∞)
b1 < b2 = 0 0 = b1 < b2 b2 = 0 b1 = 0
[a1 , a2 ] /
0/ [a2 b1 , +∞) (−∞, a2 /
b2 ] [0, +∞) (−∞, 0] (−∞, +∞)
a2 < 0
[a1 , a2 ]
(−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞)
a1 ≤ 0 ≤ a2
[a1 , a2 ] /
0/ (−∞, a1 /
b1 ] [a1 b2 , +∞) (−∞, 0] [0, +∞) (−∞, +∞)
a1 > 0
(−∞, a2 ] /
0/ [a2 b1 , +∞) (−∞, a2 /
b2 ] [0, +∞) (−∞, 0] (−∞, +∞)
a2 < 0
(−∞, a2 ]
(−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞)
a2 > 0
[a1 , +∞)
(−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞)
a1 < 0
(a1 , +∞) /
0/ (−∞, a1 /
b1 ] [a1 b2 , +∞) (−∞, 0] [0, +∞) (−∞, +∞)
a1 > 0
(−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞) (−∞, +∞)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1623
Comparisons
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1624
Setting and Querying Rounding Mode
1 #include <iostream>
2 #include <cmath>
3 #include <cfenv>
4 #include <limits>
5
6 #pragma STDC FENV_ACCESS ON
7
8 int main() {
9 std::cout.precision(std::numeric_limits<double>::max_digits10);
10 int old_mode = std::fegetround();
11 int modes[] = {FE_TONEAREST, FE_TOWARDZERO, FE_UPWARD, FE_DOWNWARD};
12 for (auto mode : modes) {
13 if (std::fesetround(mode)) {abort();}
14 std::cout << std::sqrt(2.0) << ’\n’;
15 }
16 if (std::fesetround(old_mode)) {abort();}
17 std::cout << std::sqrt(2.0) << ’\n’;
18 }
19
20 /* Example output:
21 1.4142135623730951
22 1.4142135623730951
23 1.4142135623730952
24 1.4142135623730951
25 1.4142135623730951
26 */
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1627
Section 6.5.1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1628
Geometric Predicates
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1629
Filtered Geometric Predicates
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1630
Two-Dimensional Orientation Test
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1631
Polygon Convexity Test
c b a c
c
b a
b
a
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1632
Three-Dimensional Orientation Test
d
b
a
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1633
In-Circle Test
c
d
a b
d
v b
a c
given two line segments ab and cd and vector v, determine if, compared
to orientation of cd , orientation of ab is more close, less close, or equally
close to the orientation of v
can be determined from result of computation involving dot products
prefDir(a, b, c, d, v) = |d − c|2 ((b − a) · v)2 − |b − a|2 ((d − c) · v)2
if prefDir(a, b, c, d, v) is positive, negative, or zero, then compared to
orientation of cd , orientation of ab is more close, less close, or equally
close to orientation of v, respectively
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1635
Triangulations
Triangulation
Triangulation Invalid Triangulation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1636
Delaunay Triangulations
Delaunay
Triangulation Non-Delaunay
Delaunay Triangulation Triangulation Showing
Showing Circumcircles Violation of Circumcircle
Condition
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1637
Comments on Delaunay Triangulations
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1638
Edge Flips
vℓ e vj −→ vℓ
e′
vj
vi vi
number of different triangulations of n vertices upper bounded by
n 2
n −n 2)
2 = 2 , which is O(n
all triangulations of set of vertices have same number of edges
every triangulation reachable from every other triangulation by edge flips
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1639
Local-Delaunay Test
c
d
e
a b
given flippable edge e in triangulation with incident faces abc and dcb
(whose union is strictly convex quadrilateral), determine if e is locally
Delaunay
result of predicate can be determined using in-circle test
define:
localDelaunay(a,
( b, c, d)
1 inCircle(a, b, c, d) ≥ 0
=
0 inCircle(a, b, c, d) < 0
if localDelaunay(a, b, c, d) 6= 0, edge e is locally Delaunay
if every flippable edge in triangulation is locally Delaunay, triangulation is
Delaunay
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1640
Preferred-Directions Local-Delaunay Test
c
d
e
a b
given flippable edge e in triangulation with incident faces abc and dcb
(whose union is strictly convex quadrilateral), determine if e is locally
Delaunay with preferred directions given by vectors u and v (where u and
v are nonzero and neither parallel nor orthogonal)
result of predicate can be determined using in-circle and
preferred-direction tests
define:
α(a,
b, c, d, u, v)
1 prefDir(b, c, a, d, u) > 0
0 prefDir(b, c, a, d, u) < 0
=
1 prefDir(b, c, a, d, u) = 0 and prefDir(b, c, a, d, v) > 0
0 otherwise
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1641
Preferred-Directions Local-Delaunay Test (Continued)
define:
localPrefDirDelaunay(a,
b, c, d, u, v)
1
inCircle(a, b, c, d) > 0
= 0 inCircle(a, b, c, d) < 0
α(a, b, c, d, u, v) otherwise
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1642
Lawson Local Optimization Procedure (LOP)
e′ , mark e′ as not suspect, and mark any edges whose optimality might be
affected by flip of e as suspect
essentially, LOP simply keeps flipping (flippable) edges that are not
optimal until all edges are optimal
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1643
Finding Delaunay Triangulations with Lawson LOP
given any triangulation of set P of points, can compute Delaunay
triangulation of P using Lawson LOP
select optimality criterion as locally-Delaunay or preferred-directions
locally-Delaunay condition
when edge flipped, which edges can have their optimality affected?
let e denote edge being flipped
let q denote quadrilateral formed by union of two faces incident on e
let e′ denote edge obtained by applying edge flip to e
edges that should be marked as suspect are all flippable edges belonging
to q
for example, if edge e′ was produced by flipping edge e, would need to
mark all edges drawn with thicker line (as shown below) as suspect
e −→ e′
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1644
Section 6.5.2
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1645
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1646
References II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1647
References III
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1648
Section 6.6
Cache-Efficient Code
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1649
The Memory Latency Problem
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1650
Section 6.6.1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1651
Principle of Locality
locality of reference: programs do not access all code or data uniformly
two basic types of locality:
1 temporal
2 spatial
temporal locality: tendency to reuse same information stored in memory
within relatively small time interval (e.g., code in loops, top of stack)
example (where accesses to i and sum have good temporal locality, due
to their repeated use in loop):
int func(int);
int sum = 0;
for (int i = 0; i < 10000; ++i) {sum += func(i);}
spatial locality: tendency to use information stored in nearby locations in
memory together (e.g., sequential code, neighbouring elements in array)
example (where accesses to neighbouring elements of a have good
spatial locality):
int a[1024];
// ...
a[42] = a[43] * a[44] + a[45];
to exploit locality, memory hierarchy is employed
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1652
Memory Hierarchy
Registers
Cache
(E.g., L1, L2, L3)
Increasing Latency
Memory
Increasing Capacity
(E.g., DRAM)
Decreasing Cost Per Byte
Secondary Storage
(E.g., Disk)
Tertiary Storage
(E.g., Tape)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1653
Caches
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1655
Block Placement
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1657
Direct-Mapped Cache Example
Memory Cache
Block B bytes Block B bytes
Number Number
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8
9
10
11
..
.
M−1
memory block i can only be placed in cache block mod(i, N), where N is
number of blocks in cache
for example, if N = 8 (as above), memory block 10 can only be placed in
cache block mod(10, 8) = 2 [recall: mod(10102 , 23 ) = 0102 = 2]
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1658
K -Way Set-Associative Cache Example
Memory Cache
Block B bytes Block B bytes
Number Number
0 0
Set 0
1 1
2 2
Set 1
3 3
4 4
Set 2
5 5
6 6
Set 3
7 7
8
9
10
11
..
.
M−1
memory block i can be placed in any of K cache blocks in set mod(i, S),
where S is number of sets
for example, if S = 4 and K = 2 (as above), memory block 10 can be
placed in any of cache blocks in set mod(10, 4) = 2 [recall:
mod(10102 , 22 ) = 102 = 2]
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1659
Fully-Associative Cache Example
Memory Cache
Block B bytes Block B bytes
Number Number
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8
9
10
11
..
.
M−1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1660
Block Identification
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1661
Decomposition of Memory Address
ηA bits
Memory Address
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1662
Block Identification
Cache Entries for ith Set (for K -Way Set Associative Cache)
need to determine if any entry matches tag and (if not fully associative)
index
first determine set in which block can be placed:
if not fully associative, determined by index
otherwise, cache only has one set
then look in this set for matching tag
if match found, cache hit; otherwise, cache miss
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1663
Block Replacement
block replacement policy: strategy used to determine which block
should be replaced (i.e., evicted) upon miss when no unused cache entry
available
in case of direct mapped cache, only one choice for block to replace so no
freedom in choice of replacement policy
in case of set-associative or fully-associative cache, have some choice in
block to replace
some commonly-used replacement policies include:
1 random
2 least recently used (LRU)
3 first-in first-out (FIFO)
4 approximate LRU
random: block to be replaced is randomly chosen (often using
pseudorandom number generator)
least-recently used (LRU): block that has not been used for longest time
is replaced
first-in first-out (FIFO): block that has been in cache longest is replaced
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1664
Write Policy
write policy: strategy used to handle writes to memory
two aspects to write policy:
1 cache-hit policy (i.e., how to handle cache hit)
2 cache-miss policy (i.e., how to handle cache miss)
two basic write-hit policies:
1 write through: information written to both block in cache and block in
lower-level memory
2 write back: information written only to block in cache; modified cache block
written to main memory only when replaced
two basic write-miss policies:
1 write allocate (a.k.a. fetch on write): write miss brings block into cache,
compulsory miss (a.k.a. cold miss): miss due to address being accessed
for first time (impossible to avoid; misses even with infinite sized cache)
capacity miss: miss due to cache not being large enough (i.e., program
working set is much larger than cache capacity resulting in block being
evicted from cache and later accessed again)
conflict miss: miss due to limited associativity (i.e., miss that would have
been avoided with fully associative cache); occurs when too many blocks
mapped to same set resulting in memory locations being mapped to same
cache entry
coherence miss: miss due to cache flushes to keep multiple caches
consistent (i.e., coherent) in multiprocessor system
true sharing miss: coherence miss that is due to multiple threads sharing
same data in cache block
false sharing miss: coherence miss that is due to threads accessing
different data that happens to reside in same cache block (i.e., cache
block is shared between threads but not data within cache block)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1666
Virtual Memory
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1667
Virtual Address Space
Physical Virtual
Address Space Address Space
Physical P bytes Physical Page Virtual P bytes Virtual Page
Address Number Address Number
0P 0 0P 0
1P 1 1P 1
2P 2 2P 2
3P 3 3P 3
4P 4 4P 4
5P 5 5P 5
6P 6 6P 6
7P 7 7P 7
.. ..
. .
(M − 1)P M−1 (N − 1)P N −1
P is page size
virtual address and physical address both decomposed into page number
and page offset
address translation only changes page number part of address
when virtual address translated to physical address, page offset does not
change
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1669
Translation Lookaside Buffer (TLB)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1670
Virtual and Physical Caches
if virtual memory employed, question arises as to whether memory
caches should use virtual or physical addressing
cache that employs physical addressing called physical cache (or
physically-addressed cache)
cache that employs virtual addressing called virtual cache (or
virtually-addressed cache)
key difference between use of virtual and physical cache is where address
translation takes place:
Virtual Physical Physical
Address Address Address
CPU TLB Physical Memory
Cache
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1672
Virtually-Indexed Physically-Tagged (VIPT) Caches
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1673
VIPT Cache Example
Virtual Address (48 bits)
Virtual Page Number (36 bits) Virtual Page Offset (12 bits)
Cache Tag (36 bits) Cache Index (6 bits) Cache Block Offset (6 bits)
TLB
Physical Page Number (24 bit) Physical Page Offset (12 bits) Cache
Cache Tags
Cache Result
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1674
Cache Performance
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1675
Intel Core i7
64-bit processor, x86-64 instruction set
36-bit physical addresses and 48-bit virtual addresses
three-level cache hierarchy; all levels use 64-byte block size; two-level TLB
L1 cache:
I cache: 32 KB 4-way set associative; D cache: 32 KB 8-way set
associative; per core, pseudo LRU replacement, virtually indexed and
physically tagged
L2 cache:
256 KB, 8-way set associative, per core, pseudo-LRU replacement,
physically indexed (and tagged)
L3 cache:
2 MB per core, 16-way set associative, pseudo-LRU replacement (with
ordered selection algorithm), physically indexed (and tagged)
first-level TLB:
I TLB: 128 entries, 4-way set associative, pseudo-LRU replacement; D TLB:
64 entries, 4-way set associative, pseudo-LRU replacement
second-level TLB:
512 entries, 4-way set associative, pseudo-LRU replacement, 4 KB page
size
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1676
ARM Cortex A8
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1677
Section 6.6.2
Cache-Efficient Algorithms
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1678
Cache-Efficient Algorithms
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1679
Code Transformations to Improve Cache Efficiency
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1680
Array Merging Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1681
Loop Interchange Example
for square matrices A and B, compute matrix product AB and add result to
matrix C
before blocking:
template <class T, int N>
void naive_multiply(const T (&a)[N][N], const T (&b)[N][N],
T (&c)[N][N]) {
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
double s = 0;
for (int k = 0; k < N; ++k) {
s += a[i][k] * b[k][j];
}
c[i][j] += s;
}
}
}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1684
Blocking Example (Continued 1)
1 1
B B B
i kk B
i
a b c
use 1 × B row sliver use B × B block update successive elements of
B times N times in succession 1 × B row sliver
key idea is that block of b brought into cache, fully utilized, then discarded
innermost loop pair (i.e., for j and k) multiplies 1 × B sliver of a by B × B
block of b and accumulates result in 1 × B sliver of c
references to a have: good spatial locality, since elements accessed
consecutively in loop for k; and good temporal locality, since each sliver
accessed B times in succession in loop for j
references to b have good temporal locality, since entire block accessed N
times in succession in loop for i
references to c have good spatial locality since each element of sliver
written in succession in loop for j
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1686
Cache-Aware Versus Cache-Oblivious Algorithms
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1687
Section 6.6.3
Cache-Oblivious Algorithms
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1688
Idealized Cache Model
n
N
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1690
Remarks on Assumption of Optimal-Replacement Policy
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1691
Cache-Oblivious Algorithms
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1692
Scanning
a0 a1 a2 a3 a4 a5 a6 a7 a8 a9
B B B
a0 a1 a2 a3 a4 a5 a6 a7 a8 a9
B B B B
cache block holds B array elements
consider scanning N elements of array in order (e.g., to compute sum or
minimum/maximum)
requires Θ(N) work (assuming work per element is O(1))
scanning N elements stored contiguously in memory incurs either
⌈N/B⌉ + 1 or ⌈N/B⌉ cache misses (i.e., Θ(N/B) cache misses)
may require one more than ⌈N/B⌉ cache misses due to arbitrary
alignment
cache oblivious and optimal
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1693
Array Reversal
1 2 3 4 5
a 0 a1 a2 a 3 a 4 a5 a6 a7 a8 a9
B B B B
cache block holds B array elements
consider reversing elements of N -element array a
use two parallel scans, one from each end of array, and each step swaps
two corresponding elements
for i in 0, 1, . . . , ⌊N/2⌋ − 1, swap a[i] and a[N − 1 − i]
requires Θ(N) work
incurs either ⌈N/B⌉ + 1 or ⌈N/B⌉ cache misses, assuming at least two
blocks fit in cache (i.e., Θ(N/B) cache misses)
cache oblivious and optimal
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1694
Naive Matrix Transposition
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1695
Naive Matrix Transposition: Performance
i
j
i j
a b
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1697
Cache-Oblivious Matrix Transposition Example
a11 a12 b11 b12
,
a21 a22 b21 b22
b11 b12
a11 a12 , a21 a22 ,
b21 b22
a11 , b11 a12 , b21 a21 , b12 a22 , b22
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1698
Cache-Oblivious Matrix Transposition: Performance
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1699
Naive Matrix Multiplication
naive matrix multiply code has following form:
1 template <class T, int m, int n, int p>
2 void multiply(const T (&a)[m][n], const T (&b)[n][p],
3 T (&c)[m][p]) {
4 for (int i = 0; i < m; ++i) {
5 for (int j = 0; j < p; ++j) {
6 T sum = T(0);
7 for (int k = 0; k < n; ++k) {
8 sum += a[i][k] * b[k][j];
9 }
10 c[i][j] = sum;
11 }
12 }
13 }
k
i k i
a b c
cache block holds B matrix elements
innermost loop (in which k varies) computes dot product of ith row of a
with kth column of b to yield (i, j)th element of c
second innermost loop (over j) changes column of b to use in dot product
with ith row of a (reusing ith row of a p times)
requires Θ(mnp) work, which is Θ(n3 ) in case of square matrices
assuming that row of a and column of b do not fit in cache simultaneously,
algorithm incurs Θ(mnp/B + n2 p/B + mp/B) cache misses, which is
Θ(n3 /B) in case of square matrices
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1701
Cache-Oblivious Matrix Multiplication
c11 c12 a a12 b11 b12
= 11
c21 c22 a21 a22 b21 b22
b11 b12 b11 b12
c11 c12 = a11 a12 c21 c22 = a21 a22
b21 b22 b21 b22
b11 b12 b11 b12
[c11 ] = a11 a12 [c12 ] = a11 a12 [c21 ] = a21 a22 [c22 ] = a21 a22
b21 b22 b21 b22
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1703
Cache-Oblivious Matrix Multiplication: Performance
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1704
Strassen’s Algorithm for Matrix Multiplication
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1706
Cache-Oblivious Fast Fourier Transform (FFT)
locations
6 compute n1 DFTs of rows of matrix recursively
7 transpose A in place to yield output array x with elements in correct order
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1707
Cache-Oblivious FFT: Performance
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1708
Section 6.6.4
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1709
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1710
References II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1711
References III
10 J.-W. Hong and H. T. Kung. I/O complexity: the red-blue pebbling game.
In Proc. of ACM Symposium on Theory of Computing, pages 326–333,
Milwaukee, WI, USA, 1981.
11 B. Jacob and T. Mudge. Virtual memory in contemporary
microprocessors.
IEEE Micro, 18(4):60–75, July 1998.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1712
Section 6.7
Vectorization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1713
Section 6.7.1
Vector Processing
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1714
Vector Processing
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1715
Scalar Versus Vector Instructions
first first
a operand a0 a1 ··· aL−1 operand
op op
second second
b operand b0 b1 ··· bL−1 operand
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1716
Vector-Memory and Vector-Register Architectures
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1717
Vector-Register Architectures
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1718
Vector Extensions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1719
Intel x86/x86-64 Streaming SIMD Extensions (SSE)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1720
Intel x86/x86-64 Advanced Vector Extensions (AVX)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1721
ARM NEON
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1722
Checking for Processor Vector Support on Linux
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1723
Section 6.7.2
Code Vectorization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1724
Vectorization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1725
Conceptualizing Loop Vectorization
can think of loop vectorization in terms of loop unrolling
consider following loop where, for simplicity, we assume n multiple of 4:
for (int i = 0; i < n; ++i) {c[i] = a[i] + b[i];}
can partially unroll loop to obtain following, where each iteration of new
loop corresponds to 4 iterations of original loop:
for (int i = 0; i < n; i += 4) {
c[i + 0] = a[i + 0] + b[i + 0]; // iteration i
c[i + 1] = a[i + 1] + b[i + 1]; // iteration i + 1
c[i + 2] = a[i + 2] + b[i + 2]; // iteration i + 2
c[i + 3] = a[i + 3] + b[i + 3]; // iteration i + 3
}
code in body of new loop can be mapped to vector operations of length 4
on vector registers v0, v1, and v2:
1 load a[i] to a[i + 3] into v0
2 load b[i] to b[i + 3] into v1
3 add v0 and v1, writing result into v2
4 store v2 into c[i] to c[i + 3]
using non-standard C++ syntax, vectorized loop can be expressed as:
for (int i = 0; i < n; i += 4)
{c[i : i + 3] = a[i : i + 3] + b[i : i + 3];}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1726
Approaches to Vectorization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1727
Auto-Vectorization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1728
GCC Compiler and Vectorization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1729
GCC Compiler Options Related to Vectorization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1730
GCC Compiler Options Related to Vectorization (Continued)
-fopenmp
enable OpenMP support (which requires GOMP library)
-fopenmp-simd
enable OpenMP SIMD support (which does not require run-time library)
-S
produce assembly language output only (instead of object code)
-fverbose-asm
enable generation of more verbose assembly language output (e.g.,
compiler version and command-line options, source-code lines associated
with assembly instructions, hints on which high-level expressions
correspond to various assembly instruction operands)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1731
Clang Compiler and Vectorization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1732
Clang Compiler Options Related to Vectorization
-fvectorize and -fno-vectorize
enable and disable loop vectorizer, respectively
-fslp-vectorize and -no-fslp-vectorize
enable and disable SLP vectorizer, respectively
-fslp-vectorize-aggressive
enable more aggressive vectorization in SLP vectorizer
-Rpass=loop-vectorize
enable remarks that identify loops that were successfully vectorized
-Rpass-missed=loop-vectorize
enable remarks that identify loops that failed vectorization and indicate if
vectorization specified
-Rpass-analysis=loop-vectorize
enable remarks that identify statements that caused vectorization to fail
-fopenmp
enable OpenMP support (which requires OMP library)
-S
produce assembly language output only (instead of object code)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1733
Assessing Quality of Vectorized Code
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1734
Assessing Quality of Vectorized Code (Continued)
1 .file "inner_product_1.cpp"
2 .text
3 .globl _Z9innerprodPfS_i
4 .type _Z9innerprodPfS_i, @function
5 _Z9innerprodPfS_i:
6 .LFB0:
7 .cfi_startproc
8 #APP
9 # 3 "inner_product_1.cpp" 1
10 # loop start
11 # 0 "" 2
12 #NO_APP
13 xorl %eax, %eax
14 vxorps %xmm0, %xmm0, %xmm0
15 .L3:
16 cmpl %eax, %edx
17 jle .L2
18 vmovss (%rdi,%rax,4), %xmm1
19 vfmadd231ss (%rsi,%rax,4), %xmm1, %xmm0
20 incq %rax
21 jmp .L3
22 .L2:
23 #APP
24 # 5 "inner_product_1.cpp" 1
25 # loop end
26 # 0 "" 2
27 #NO_APP
28 ret
29 .cfi_endproc
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1735
Auto-Vectorization with Hints
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1736
Obstacles to Vectorization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1737
Data Dependencies and Vectorization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1738
Flow Dependencies
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1739
Flow Dependence Example
consider vectorization of following loop with vectorization factor of 4:
for (int i = 1; i < n; ++i)
{a[i] = a[i - 1] + b[i];}
loop exhibits flow dependence (i.e., read after write) on a[i-1]
(dependence distance 1)
loop in partially unrolled form (assuming number of iterations multiple of
4):
for (int i = 1; i < n; i += 4) {
a[i + 0] = a[i - 1] + b[i + 0];
a[i + 1] = a[i + 0] + b[i + 1];
a[i + 2] = a[i + 1] + b[i + 2];
a[i + 3] = a[i + 2] + b[i + 3];
}
loop in vectorized form (assuming number of iterations multiple of 4):
for (int i = 1; i < n; i += 4)
{a[i : i + 3] = a[i - 1 : i + 2] + b[i : i + 3];}
vectorized loop will not always produce same results as sequential loop
(due to flow dependence with dependence distance 1)
therefore, with vectorization factor of 4, loop not legal to vectorize
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1740
Flow Dependence Example: Sequential Loop
suppose that:
constexpr int n = 5;
int a_data[n] = {-1, -2, -3, -4, -5};
int b_data[n] = {0, 1, 2, 3, 4};
int* a = a_data;
int* b = b_data;
sequential loop:
for (int i = 1; i < n; ++i) {
a[i] = a[i - 1] + b[i]
}
computation for loop iteration:
i a[i - 1] b[i] a[i]
1 -1 1 0
2 0 2 2
3 2 3 5
4 5 4 9
upon loop termination, array pointed to by a contains:
{-1, 0, 2, 5, 9}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1741
Flow Dependence Example: Vectorized Loop
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1742
Flow Dependence Example
consider vectorizing following loop using vectorization factor of 4:
for (int i = 5; i < n; ++i)
{a[i] = a[i - 5] + b[i];}
loop exhibits flow dependence (i.e., read after write) on a[i-5]
(dependence distance 5)
loop in partially unrolled form (assuming number of iterations multiple of
4):
for (int i = 5; i < n; i += 4) {
a[i + 0] = a[i - 5] + b[i + 0];
a[i + 1] = a[i - 4] + b[i + 1];
a[i + 2] = a[i - 3] + b[i + 2];
a[i + 3] = a[i - 2] + b[i + 3];
}
loop in vectorized form (assuming number of iterations multiple of 4):
for (int i = 5; i < n; i += 4)
{a[i : i + 3] = a[i - 5 : i - 2] + b[i : i + 3];}
vectorized loop will always yield same result as sequential loop since no
flow dependence occurs within single iteration of vectorized loop
with vectorization factor of 4, loop legal to vectorize
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1743
Output Dependencies
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1744
Control-Flow Dependencies and Vectorization
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1745
Aliasing
p q
aliasing often limits ability of compiler to perform optimization
in effect, aliasing can introduce new data dependencies that would not
otherwise exist
failing to take aliasing into account could lead to illegal optimizations (i.e.,
optimizations that change code behavior)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1746
Aliasing and Optimization: An Example
consider code:
1 void func(int* a, int* b, int* c) {
2 *a = 42;
3 *b = 0;
4 *c = *a;
5 }
at first glance, might seem that code can be optimized to yield:
1 void func(int* a, int* b, int* c) {
2 *a = 42;
3 *b = 0;
4 *c = 42;
5 }
above optimized code is incorrect, since a might equal b, in which case *c
should be assigned 0, not 42
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1747
Aliasing and Vectorization: An Example
consider code:
1 void add(float* a, float* b, float* c) {
2 for (int i = 0; i < 1024; ++i) {
3 a[i] = b[i] + c[i];
4 }
5 }
if only this code visible to compiler, simply vectorizing loop in this function
is not legal
a could be aliased to b or c (i.e., storage pointed to by a, b, and c could
overlap)
in this case, sequential and parallel execution of loop would yield different
results
best compiler could do might be to:
generate two different versions of code for loop, one without vectorization
for aliasing case and one with vectorization for case of no aliasing
emit runtime aliasing check that decides which version of code for loop to
execute
this solution less than ideal as it incurs cost of runtime check and results
in increased code size
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1748
The __restrict__ Keyword
sometimes highly beneficial to have means to indicate to compiler that
aliasing cannot occur (so that compiler can better optimize code)
although not part of C++ standard, some compilers support special
keyword for this purpose; for example:
GCC and Clang support __restrict__ keyword
MSVC supports __restrict keyword
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1750
Data Alignment
for reasons of performance, vector load and store operations often impose
restrictions on data alignment
typically, target address for vector load or store of n-byte register needs to
be aligned on n-byte boundary
for some architectures, such alignment is strict requirement (i.e., code will
not work if data misaligned)
for other architectures, such alignment is not strictly required, but
substantial performance penalty may be incurred in case of misaligned
data
for this reason, important to align data appropriately whenever possible
also, to allow compiler to vectorize in most effective manner possible,
important to let compiler know when data is appropriately aligned
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1751
Handling Misaligned Data
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1752
Controlling Alignment of Data
for non-heap allocation, can use alignas qualifier to control alignment
of object
for heap allocation, can use std::aligned_alloc to allocate memory
with particular alignment
std::free can be used to free memory allocated by
std::aligned_alloc
example:
1 #include <cassert>
2 #include <cstdlib>
3 #include <cstdint>
4
5 int main() {
6 alignas(4096) static char buffer[65536];
7 static_assert(alignof(buffer) == 4096);
8 float* fp = static_cast<float*>(
9 std::aligned_alloc(4096, sizeof(float)));
10 if (!fp) {return 1;}
11 assert(!(reinterpret_cast<intptr_t>(fp) % 4096));
12 std::free(fp);
13 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1753
Informing Compiler of Data Alignment
to facilitate more effective vectorization by compiler, important to be able
to indicate data alignment in code
unfortunately, C++ standard does not provide mechanism for doing this
some compilers (such as GCC and Clang) support intrinsic function called
__builtin_assume_aligned that can be used to indicate alignment
__builtin_assume_aligned declared as:
void* __builtin_assume_aligned(const void *p, size_t align, ...);
this function simply returns its first argument p and allows compiler to
assume that returned pointer is at least align bytes aligned (when
invoked with two arguments)
example:
void func(float* a, float* b, int n) {
// *a and *b can be assumed aligned to 64-byte boundary
a = static_cast<float*>(__builtin_assume_aligned(a, 64));
b = static_cast<float*>(__builtin_assume_aligned(b, 64));
for (int i = 0; i < n; ++i) {/* ... */}
}
in case of compilers that do not support __builtin_assume_aligned,
another approach would need to be found
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1754
Profitability of Vectorization
vectorization can often provide significant speedup (in some cases linear
with vectorization factor), but costs need to be considered
vector loop bodies can be larger than their scalar forms, as more complex
operations may be needed, increasing code size
vector loop may have increased startup costs to prepare for vectorized
execution
if aliasing is potential problem, require overhead of runtime aliasing check
vector instructions may take more cycles
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1755
Vectorization Example (Version 1)
source code:
1 #include <cstddef>
2
3 template <std::size_t n, class T>
4 void add(const T (&a)[n], T (&b)[n]) {
5 for (int i = 0; i < n; ++i) {
6 b[i] += a[i];
7 }
8 }
since a and b may be aliased, compiler must generate code that correcly
handles aliased case (as well as non-aliased case)
often, will generate code that tests for aliasing at run time and uses result
to decide between code for aliased case or non aliased case
since compiler does not know alignment of a and b, must generate code
that handles any valid alignment
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1756
Vectorization Example (Version 2)
source code:
1 #include <cstddef>
2
3 template <std::size_t n, class T>
4 void add(const T (&__restrict__ a)[n],
5 T (&__restrict__ b)[n]) {
6 for (int i = 0; i < n; ++i) {
7 b[i] += a[i];
8 }
9 }
compiler can assume no aliasing (due to use of __restrict__)
since compiler does not know alignment of a and b, must generate code
that handles any valid alignment
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1757
Vectorization Example (Version 3)
source code:
1 #include <cstddef>
2
3 template <std::size_t n, std::size_t align, class T>
4 void add(const T (& __restrict__ a)[n],
5 T (& __restrict__ b)[n]) {
6 const T* ap = static_cast<const T*>(
7 __builtin_assume_aligned(&a, align));
8 T* bp = static_cast<T*>(
9 __builtin_assume_aligned(&b, align));
10 for (int i = 0; i < n; ++i) {
11 bp[i] += ap[i];
12 }
13 }
compiler can assume no aliasing (due to use of __restrict__) and
align-byte alignment (due to use of __builtin_assume_aligned)
code generated for vectorized loop in case of
add<65536, 16 * alignof(float), float>:
12 .L2:
13 vmovaps (%rsi,%rax), %ymm0
14 vaddps (%rdi,%rax), %ymm0, %ymm0
15 vmovaps %ymm0, (%rsi,%rax)
16 addq $32, %rax
17 cmpq $262144, %rax
18 jne .L2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1758
Vectorization Example
when using add function, must be careful to ensure that assumptions
about data alignment are not violated
source code:
1 #include <cstddef>
2 #include <iostream>
3 #include <algorithm>
4 #include <numeric>
5 #include "example4_util.hpp"
6
7 int main() {
8 constexpr std::size_t n = 65536;
9 constexpr std::size_t align = 16 * alignof(float);
10 alignas(align) static float a[n];
11 alignas(align) static float b[n];
12 std::iota(&a[0], &a[n], 1);
13 std::fill(&b[0], &b[n], -1);
14 add<n, align>(a, b);
15 for (auto i : b) {std::cout << i << ’\n’;}
16 }
if code does not ensure correct alignment of data, code will not work
correctly (and probably will result in crash)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1759
Basic Requirements for Vectorizable Loops
countable: number of loop iterations known at run time upon entry to loop
(e.g., implies no conditional termination of loop)
straight-line code (i.e., no control flow); no switch statements; if statements
only allowable when can be implemented as masked assignments
must be innermost loop if nested
no function calls, except some basic math functions (such as std::pow,
std::sqrt, and std::sin) and some inline functions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1760
OpenMP SIMD Constructs
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1761
OpenMP simd Pragma
vectorized loop can be achieved with OpenMP simd pragma
syntax:
#pragma omp simd [clause. . .]
/* for statement in canonical form */
simd pragma must be immediately followed by for loop in canonical form
optional clauses may be specified to affect behavior of pragma (i.e.,
safelen, linear, aligned, private, lastprivate, reduction, and
collapse)
amongst other things, canonical form of for loop implies:
induction variable has integer, pointer, or random-access iterator type
limited test and increment/decrement for induction varaible
iteration count known before execution of loop
can target inner or outer loops
loop must be suitable for vectorization (e.g., no data-dependence
problems)
example:
#pragma omp simd
for (int i = 0; i < n; ++i) {c[i] = a[i] + b[i];}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1762
OpenMP declare simd Pragma
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1763
OpenMP SIMD-Related Pragma Clauses
safelen(length)
specifies length as maximum number of iterations that can be run
concurrently in safe manner (i.e., without data-dependence problems)
collapse(n)
specifies how many (nested) loops to associate with loop construct (i.e.,
how many nested loops to combine)
simdlen(length)
specifies length as prefered length of vector registers used
aligned(argument-list[:alignment])
specifies items in argument-list as having given alignment (e.g., alignment)
uniform(argument-list)
indicates each argument in argument-list has constant value between
iterations of given loop (i.e., constant value across all SIMD lanes)
inbranch
specifies that function will always be called from inside conditional
statement of SIMD loop
notinbranch
specifies that function will never be called from inside conditional statement
of SIMD loop
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1764
OpenMP SIMD-Related Pragma Clauses (Continued)
linear(list[:linear-step])
specifies that, for every iteration of original scalar loop, each variable in list
is incremented by particular step step (i.e., variable is incremented by step
times vector length for vectorized loop)
private(list)
declares variables in list to be private to each iteration
lastprivate(list)
declares variables in list to be private to each iteration, and last value is
copied out from last iteration instance
reduction(operator:list)
specifies variables in list are reduction variables for operator operator
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1765
Example: Vectorized Loop
1 #include <cstddef>
2 #include <iostream>
3 #include <numeric>
4
5 template <std::size_t align, std::size_t n, class T>
6 [[ gnu::noinline ]]
7 void multiply(const T (&a)[n], const T (&b)[n], T (&c)[n]) {
8 #pragma omp simd aligned(a, b, c : align)
9 for (int i = 0; i < n; ++i) {
10 c[i] = a[i] * b[i];
11 }
12 }
13
14 int main() {
15 constexpr std::size_t n = 65536;
16 constexpr std::size_t align = 16 * alignof(float);
17 alignas(align) static float a[n];
18 alignas(align) static float b[n];
19 alignas(align) static float c[n];
20 std::iota(a, &a[n], 0);
21 std::iota(b, &b[n], 0);
22 multiply<align>(a, b, c);
23 for (auto x : c) {
24 std::cout << x << ’\n’;
25 }
26 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1766
Example: Vectorized Loop and Function
1 #include <cstddef>
2 #include <iostream>
3 #include <numeric>
4
5 #pragma omp declare simd notinbranch
6 float func(float a, float b) {
7 return a * a + b * b;
8 }
9
10 int main() {
11 constexpr std::size_t n = 65536;
12 constexpr std::size_t align = 16 * alignof(float);
13 alignas(align) static float a[n];
14 alignas(align) static float b[n];
15 alignas(align) static float c[n];
16 std::iota(a, &a[n], 0);
17 std::iota(b, &b[n], 0);
18 #pragma omp simd aligned(a, b, c : align)
19 for (int i = 0; i < n; ++i) {
20 c[i] = func(a[i], b[i]);
21 }
22 for (auto x : c) {
23 std::cout << x << ’\n’;
24 }
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1767
Section 6.7.3
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1768
Talks I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1769
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1770
Section 6.8
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1771
Documentation for Software Development
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1772
Software Requirements Specification (SRS)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1773
SRS (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1774
External Interfaces
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1775
Benefits of SRS
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1776
SRS Example: Sorting Program
single program that performs sorting
given records as input, program sorts records and outputs records in
sorted order
record data format (for input and output):
records delimited by single newline character
each record consists of one or more fields, separated by one or more
whitespace characters
restrictions/constraints:
may assume sufficient memory to buffer all records
software must work without any modification to source code on any platform
with C++ compiler compliant with C++11 standard
records read from standard input
sorted records written to standard output
any error/warning messages written to standard error
sorts records using nth field in record as key
can sort in ascending or descending order
sort key may be numeric or string
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1777
SRS Example: Sorting Program (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1778
Software Design Description (SDD)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1779
SDD (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1780
Benefits of SDD
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1781
SDD Example: Sorting Program
Key alias for type that represents sort key (alias for std::string)
Compare functor class for comparing Key objects
Dataset class represents collection of all records
specify all class interfaces (i.e., public members)
Dataset class provides:
constructor that creates dataset by reading all records from input stream
function to output all records in sorted order to output stream
Dataset class to use std::multimap<Key, std::string, Compare>
allows n records to be sorted in O(n log n) time [n insertions, each
requiring O(log n) time]
handling n records requires O(n) memory
only uses C++ standard library
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1782
Requirements/Design Document for Degree Project
document is combination of SRS and SDD with some added information
about testing strategies
briefly introduce problem being addressed by software
describe each program and library to be developed
identify parts of any external software (e.g., programs or libraries) that will
be used
describe user interface (e.g., CLI, GUI) for each program
fully specify all data formats used
describe overall structure of each program and library
identify all key data structures and algorithms to be used
provide pseudocode for key parts of the software
state any potentially limiting assumptions made by software
indicate how programs and library code will be tested
offer any other information that may be helpful (since above list is not
exhaustive)
provide sufficient detail for other people to understand how software is to
be structured and how it will be implemented and tested
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1783
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1784
Part 7
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1785
Section 7.1
Debuggers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1786
Source-Level Debuggers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1787
GNU Debugger (GDB)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1788
gdb Commands
help
Print help information.
quit
Exit debugger.
run [arglist]
Start the program (with arglist if specified).
print expr
Display the value of the expression expr.
bt
Display a stack backtrace.
list
Type the source code lines in the vicinity of where the program is currently
stopped.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1789
gdb Commands (Continued)
break function
Set a breakpoint at the function function.
watch expr
Set a watchpoint for the expression expr.
c
Continue running the program (e.g., after stopping at a breakpoint).
next
Execute the next program line, stepping over any function calls in the line.
step
Execute the next program line, stepping into any function calls in the line.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1790
GNU Data Display Debugger (DDD)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1791
Section 7.2
Code Sanitizers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1792
Code Sanitizers
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1793
Address Sanitizer (ASan)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1794
Using Address Sanitizer
allocator_may_return_null
check_initialization_order
detect_stack_use_after_return
new_delete_type_mismatch
exitcode
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1795
Out-of-Bounds Access to Globals
global_buffer_overflow.cpp
1 #include <iostream>
2 int a[4] = {1, 2, 3, 4};
3 int main() {
4 for (int i = 0; i <= 4; ++i) {
5 std::cout << a[i] << ’\n’;
6 }
7 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1796
Out-of-Bounds Access to Stack
stack_buffer_overflow.cpp
1 #include <iostream>
2 int main() {
3 int a[4] = {1, 2, 3, 4};
4 for (int i = 0; i <= 4; ++i)
5 {std::cout << a[i] << ’\n’;}
6 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1797
Out-of-Bounds Access to Heap
heap_buffer_overflow.cpp
1 #include <iostream>
2 #include <cstring>
3 int main() {
4 char* p = new char[5];
5 std::strcpy(p, "Hello");
6 std::cout << p << ’\n’;
7 delete[] p;
8 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1799
Stack Use After Return
stack_use_after_return.cpp
1 int* g = nullptr;
2 void foobar() {int i = 42; g = &i;}
3 int main() {
4 foobar();
5 return *g;
6 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1800
Stack Use After Scope
use_after_scope.cpp
1 #include <iostream>
2 int main() {
3 int* p;
4 {int x = 0; p = &x;}
5 std::cout << *p << ’\n’;
6 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1801
Double Free
double_free.cpp
1 int main() {
2 int* p = new int[16];
3 delete[] p;
4 delete[] p;
5 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1802
Memory Leaks
memory_leak.cpp
1 #include <iostream>
2 #include <cstring>
3 int main() {
4 char* p = new char[1024];
5 std::strcpy(p, "Hello, World!\n");
6 std::cout << p;
7 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1803
Initialization Order Problems
init_order_main.cpp
1 #include <iostream> init_order_other.cpp
2 extern int B; 1 #include <cstdlib>
3 int A = B; 2 int B = std::atoi("42");
4 int main()
5 {std::cout << A << ’\n’;}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1804
Thread Sanitizer (TSan)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1805
Using Thread Sanitizer
history_size
suppressions
exitcode
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1806
Data Race
data_race.cpp
1 #include <thread>
2 int x = 0;
3 int main() {
4 std::thread t([&]{x = 42;});
5 x = 43;
6 t.join();
7 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1807
Deadlock
deadlock.cpp
1 #include <iostream>
2 #include <thread>
3 #include <mutex>
4 std::mutex m0;
5 std::mutex m1;
6 void func1(int n) {
7 for (auto i = n; i > 0; --i) {
8 std::lock_guard<std::mutex> l0(m0);
9 std::lock_guard<std::mutex> l1(m1);
10 std::cout << "a\n";
11 }
12 }
13 void func2(int n) {
14 for (auto i = n; i > 0; --i) {
15 std::lock_guard<std::mutex> l1(m1);
16 std::lock_guard<std::mutex> l0(m0);
17 std::cout << "b\n";
18 }
19 }
20 int main() {
21 std::thread t1([]{func1(1);});
22 std::thread t2([]{func2(1);});
23 t1.join(); t2.join();
24 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1808
Deadlock (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1809
Memory Sanitizer (MSan)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1810
Using Memory Sanitizer
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1811
Read From Uninitialized Memory
uninitialized_1.cpp
1 int main(int argc, char** argv) {
2 int x[2];
3 x[0] = 1;
4 if (x[argc % 2]) {
5 return 1;
6 }
7 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1812
Undefined-Behavior Sanitizer (UBSan)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1813
Using Undefined-Behavior Sanitizer
strip_path_prefix
verbosity
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1814
Signed Integer Overflow
signed_integer_overflow.cpp
1 #include <iostream>
2 #include <limits>
3 int main() {
4 int x = std::numeric_limits<int>::max();
5 int y = x + 1;
6 std::cout << y << ’\n’;
7 }
program output:
signed_integer_overflow.cpp:5:14: runtime error: signed integer
overflow: 2147483647 + 1 cannot be represented in type ’int’
-2147483648
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1815
Invalid Shift
invalid_shift.cpp
1 #include <iostream>
2 int main() {
3 int x = 32678;
4 int y = 1 << x;
5 std::cout << y << ’\n’;
6 }
program output:
invalid_shift.cpp:4:12: runtime error: shift exponent 32678 is too
large for 32-bit type ’int’
0
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1816
Leak Sanitizer (LSan)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1817
Memory Leak
heap_buffer_overflow.cpp
1 #include <iostream>
2 #include <cstring>
3 int main() {
4 char* p = new char[1024];
5 std::strcpy(p, "Hello, World!\n");
6 std::cout << p;
7 }
program output:
Hello, World!
=================================================================
==10786==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 1024 byte(s) in 1 object(s) allocated from:
#0 0x7faa5e0a7436 in operator new[](unsigned long) ../../../../src/
libsanitizer/lsan/lsan_interceptors.cc:164
#1 0x400894 in main memory_leak.cpp:4
SUMMARY: LeakSanitizer: 1024 byte(s) leaked in 1 allocation(s).
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1818
Section 7.2.1
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1819
Talks I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1820
Section 7.3
Clang-Tidy
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1821
Clang-Tidy
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1822
The clang-tidy Command
to generate compile-commands file (i.e., compile_commands.json), add
following option to cmake command:
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
command line has following form:
clang-tidy [options] [$source_file]...
some options include:
Option Description
-checks=string specify check to include/exclude
-p build path set build path to build path
-version print version information and exit
-help print help information and exit
-list-checks list all enabled checks and exit
-fix apply suggested fixes
-fix-errors apply suggested fixes even if com-
pilation errors found
-warnings-as-errors=string treat specified warnings as errors
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1823
Some Supported Checks
uninitialized arguments
dereferencing null pointers
division by zero
address of stack memory that escape function
undefined result of binary operator
uninitialized array subscript
assigning uninitialized values
uninitialized branch condition
blocks that capture uninitialized values
uninitialized value being returned from function
value-returning function that does not return value
new-delete mismatch
dead code (e.g., dead stores)
use of unsafe functions (e.g., getpw, gets, mktemp, strcpy, strcat)
use of inferior random number generating functions (e.g., drand48)
some specific examples on subsequent slides
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1824
Division By Zero
divide_by_zero.cpp
1 int func(int x) {
2 if (!x) {
3 return 1024 / x;
4 } else {
5 return x;
6 }
7 }
clang-tidy output:
divide_by_zero.cpp:3:15: warning: Division by zero [clang-analyzer-core
.DivideZero]
return 1024 / x;
ˆ
divide_by_zero.cpp:2:6: note: Assuming ’x’ is 0
if (!x) {
ˆ
divide_by_zero.cpp:2:2: note: Taking true branch
if (!x) {
ˆ
divide_by_zero.cpp:3:15: note: Division by zero
return 1024 / x;
ˆ
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1825
New-Delete Mismatch
new_delete_mismatch.cpp
1 int main() {
2 char* p = new char[1024];
3 delete p;
4 }
clang-tidy output:
new_delete_mismatch.cpp:3:2: warning: ’delete’ applied to a pointer that was
allocated with ’new[]’; did you mean ’delete[]’? [clang-diagnostic-
mismatched-new-delete]
delete p;
ˆ
[]
new_delete_mismatch.cpp:2:12: note: allocated with ’new[]’ here
char* p = new char[1024];
ˆ
new_delete_mismatch.cpp:3:2: warning: Memory allocated by ’new[]’ should be
deallocated by ’delete[]’, not ’delete’ [clang-analyzer-unix.
MismatchedDeallocator]
delete p;
ˆ
new_delete_mismatch.cpp:2:12: note: Memory is allocated
char* p = new char[1024];
ˆ
new_delete_mismatch.cpp:3:2: note: Memory allocated by ’new[]’ should be
deallocated by ’delete[]’, not ’delete’
delete p;
ˆ
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1826
Missing Return Statement
no_return.cpp
1 int func(int x) {
2 if (x >= 0) {
3 return 1;
4 }
5 }
clang-tidy output:
no_return.cpp:5:1: warning: control may reach end of non-void function
[clang-diagnostic-return-type]
}
ˆ
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1827
Stack Address Escapes Function
stack_address_escape.cpp
1 int* p;
2
3 void test() {
4 int x = 42;
5 p = &x;
6 }
clang-tidy output:
stack_address_escape.cpp:6:1: warning: Address of stack memory
associated with local variable ’x’ is still referred to by the
global variable ’p’ upon returning to the caller. This will be a
dangling reference [clang-analyzer-core.StackAddressEscape]
}
ˆ
stack_address_escape.cpp:6:1: note: Address of stack memory associated
with local variable ’x’ is still referred to by the global variable
’p’ upon returning to the caller. This will be a dangling
reference
}
ˆ
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1828
Undefined Operand
undefined_operand.cpp
1 int test() {
2 int x;
3 return x + 1;
4 }
clang-tidy output:
undefined_operand.cpp:3:11: warning: The left operand of ’+’ is a
garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult]
return x + 1;
ˆ
undefined_operand.cpp:2:2: note: ’x’ declared without an initial value
int x;
ˆ
undefined_operand.cpp:3:11: note: The left operand of ’+’ is a garbage
value
return x + 1;
ˆ
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1829
Section 7.3.1
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1830
Talks I
1 Daniel Jasper, Keep Your Code Sane With Clang Tidy, Meeting C++,
Berlin, Germany, Dec. 4–5, 2015. Available online at https://youtu.
be/nzCLcfH3pb0.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1831
Section 7.4
Valgrind
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1832
Valgrind
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1833
Section 7.4.1
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1834
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1835
Section 7.5
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1836
Gcov
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1837
LLVM Cov
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1838
Using Gcov
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1839
Using Gcov (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1840
Example: Source Code
example_main.cpp
1 #include <iostream>
2 #include <cstdlib>
3 double signum(double x);
4 int main(int argc, char** argv) {
5 if (argc < 2) {
6 return 1;
7 }
8 double x = std::atof(argv[1]);
9 std::cout << signum(x) << ’\n’;
10 }
example_signum.cpp
1 double signum(double x) {
2 if (x > 0) {
3 return 1.0;
4 } else if (x < 0) {
5 return -1.0;
6 } else {
7 return 0.0;
8 }
9 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1841
Example: Build and Run Program and Run Gcov
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1842
Example: Gcov Output
example_main.cpp.gcov
-: 0:Source:example_main.cpp
-: 0:Programs:2
-: 1:#include <iostream>
-: 2:#include <cstdlib>
-: 3:double signum(double x);
2: 4:int main(int argc, char** argv) {
2: 5: if (argc < 2) {
1: 6: return 1;
-: 7: }
1: 8: double x = std::atof(argv[1]);
1: 9: std::cout << signum(x) << ’\n’;
7: 10:}
example_signum.cpp.gcov
-: 0:Source:example_signum.cpp
-: 0:Programs:2
1: 1:double signum(double x) {
1: 2: if (x > 0) {
1: 3: return 1.0;
#####: 4: } else if (x < 0) {
#####: 5: return -1.0;
-: 6: } else {
#####: 7: return 0.0;
-: 8: }
-: 9:}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1843
Lcov
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1844
Using Lcov
build project with GCC or Clang and ensure that --coverage option is
used for compiling and linking as in earlier Gcov example
run program to collect coverage data as in earlier Gcov example
process coverage data with Lcov; for example, using command like:
lcov --capture --directory . --output-file coverage.info
generate HTML output using genhtml; for example, using command like:
genhtml coverage.info --output-directory output
view in browser; for example, using command like:
firefox output/index.html
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1845
Example: Lcov Output
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1846
Example: Lcov Output (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1847
Section 7.5.1
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1848
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1849
Section 7.6
Catch2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1850
Catch2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1851
Counter Class Example: counter Class
1 #include <stdexcept>
2 #include <numeric>
3
4 class counter {
5 public:
6 using count_type = std::size_t;
7 static constexpr count_type max_count()
8 {return std::numeric_limits<count_type>::max();}
9 counter(count_type count = 0) : count_(count) {}
10 count_type get_count() const {return count_;}
11 void increment() {
12 if (count_ == max_count())
13 {throw std::overflow_error("counter overflow");}
14 ++count_;
15 }
16 private:
17 count_type count_;
18 };
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1852
Counter Class Example: Test Code
1 #define CATCH_CONFIG_MAIN
2 #include <catch/catch.hpp>
3 #include "counter.hpp"
4
5 TEST_CASE("constructor", "[counter]") {
6 counter x;
7 CHECK(x.get_count() == 0);
8 counter y(1);
9 CHECK(y.get_count() == 1);
10 }
11
12 TEST_CASE("maximum count", "[counter]") {
13 CHECK(counter::max_count() == std::numeric_limits<
14 counter::count_type>::max());
15 }
16
17 TEST_CASE("increment (no overflow)", "[counter]") {
18 counter x(0);
19 REQUIRE(x.get_count() == 0);
20 x.increment();
21 CHECK(x.get_count() == 1);
22 }
23
24 TEST_CASE("increment (overflow)", "[counter]") {
25 counter x(counter::max_count());
26 CHECK_THROWS_AS(x.increment(), std::overflow_error);
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1853
Approximate Comparison Example
1 #define CATCH_CONFIG_MAIN
2 #include <catch/catch.hpp>
3
4 TEST_CASE("addition") {
5 float x = 0.0f;
6 for (int i = 0; i < 10; ++i) {
7 x += 0.1f;
8 }
9 CHECK(x == 1.0f);
10 // may fail due to roundoff error
11 CHECK(x == Approx(1.0f));
12 // should pass
13 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1854
Talks I
1 Phil Nash, Modern C++ Testing with Catch2, Meeting C++, Berlin,
Germany, Nov. 9, 2017. Available online at https://youtu.be/
3tIE6X5FjDE.
2 Phil Nash, Modern C++ Testing with Catch2, C++ Edinburgh, Edinburgh,
UK, Aug. 14, 2017. Available online at https://youtu.be/
grC0S6ZK59U.
3 Phil Nash, Test Driven C++ with Catch, CppCon, Bellevue, WA, USA,
Sept. 22, 2015. Available online at https://youtu.be/gdzP3pAC6UI.
4 Phil Nash, Testdriven C++ with Catch, Meeting C++, Berlin, Germany,
Dec. 5–6, 2014. Available online at https://youtu.be/C2LcIp56i-8.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1855
Part 8
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1856
Section 8.1
Perf
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1857
Linux Kernel Perf Event Interface
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1858
Perf
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1859
Events
hardware event:
event measurable by performance monitoring unit (PMU) of processor
examples: CPU cycles (cycles) and cache misses (cache-misses)
hardware cache event:
event measurable by PMU of processor
examples: L1 data cache load misses (L1-dcache-load-misses) and
data translation-lookaside-buffer load misses (dTLB-load-misses)
software event:
low-level events based on kernel counters
examples: CPU clock (cpu-clock) and page fault (page-faults)
kernel tracepoint event:
predefined static instrumentation points in kernel code where trace
information can be collected
examples: entering open system call (syscalls:sys_enter_open) and
context switch (sched:sched_switch)
probe event:
user-defined events dynamically inserted into kernel
created using uprobes or kprobes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1860
Some Events
Event Description
cache-misses cache misses
cache-references cache accesses
cycles CPU cycles
cpu-clock CPU wall-time clock
instructions CPU instructions
cs context switches
faults page faults
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1861
Stack Traces
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1862
Event-Based Sampling
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1864
Event Specifiers (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1865
Hardware Event Skid
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1866
The perf Program
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1867
Perf List Command
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1868
Perf List Example
$ perf list
List of pre - defined events ( to be used in -e ):
branch - instructions OR branches [ Hardware event ]
branch - misses [ Hardware event ]
bus - cycles [ Hardware event ]
cache - misses [ Hardware event ]
cache - references [ Hardware event ]
cpu - cycles OR cycles [ Hardware event ]
instructions [ Hardware event ]
ref - cycles [ Hardware event ]
[ text deleted ]
alignment - faults [ Software event ]
context - switches OR cs [ Software event ]
cpu - clock [ Software event ]
cpu - migrations OR migrations [ Software event ]
[ text deleted ]
L1 - dcache - load - misses [ Hardware cache event ]
L1 - dcache - loads [ Hardware cache event ]
L1 - dcache - prefetch - misses [ Hardware cache event ]
L1 - dcache - store - misses [ Hardware cache event ]
L1 - dcache - stores [ Hardware cache event ]
L1 - icache - load - misses [ Hardware cache event ]
[ text deleted ]
cache - misses OR cpu / cache - misses / [ Kernel PMU event ]
cache - references OR cpu / cache - references / [ Kernel PMU event ]
cpu - cycles OR cpu /cpu - cycles / [ Kernel PMU event ]
instructions OR cpu / instructions / [ Kernel PMU event ]
mem - loads OR cpu /mem - loads / [ Kernel PMU event ]
mem - stores OR cpu /mem - stores / [ Kernel PMU event ]
[ text deleted ]
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1869
Perf Stat Command
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1870
Perf Stat Example
$ perf stat dd if =/ dev / urandom of =/ dev / null bs =1 K count =32 K status = none
Performance counter stats for
’dd if =/ dev / urandom of =/ dev / null bs =1 K count =32 K status = none ’:
1727.055828 task - clock ( msec ) # 0.999 CPUs utilized
1 context - switches # 0.001 K/ sec
13 cpu - migrations # 0.008 K/ sec
60 page - faults # 0.035 K/ sec
5 ,805 ,261 ,702 cycles # 3.361 GHz
2 ,115 ,865 ,103 stalled - cycles - frontend # 36.45% frontend
cycles idle
<not supported > stalled - cycles - backend
12 ,108 ,757 ,065 instructions # 2.09 insns per cycle
# 0.17 stalled cycles
per insn
254 ,471 ,634 branches # 147.344 M/ sec
257 ,282 branch - misses # 0.10% of all branches
1.728232622 seconds time elapsed
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1871
Perf Record Command
run command and record its profile into Perf data file
command line interface has following form:
perf record [options] command [args]
some common options include:
Option Description
-e event specify event name
-a collect data from all processors
-p pid collect data from existing process ID pid
-t tid collect data from existing thread ID tid
-C cpu collect data from CPUs cpu
-c count set event count between samples to count
-o file set output file to file
-F freq set sampling frequency to approximately freq
-g enable call graph (i.e., stack trace) recording
output file defaults to perf.data
by default, uses cycles event with sampling frequency set to
version-dependent value (typically, 1000 Hz to 4000 Hz)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1872
Perf Record Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1873
Perf Report Command
read Perf data (created by Perf record) and display profile
command line interface has following form:
perf report [options]
some common options include:
Option Description
-i file set input file to file
-v increase verbosity level
-n show number of samples for each symbol
-C cpu only show events for CPU cpu
--pid pid only show events for process ID pid
--tid tid only show events for thread ID tid
-d dsos only consider symbols in DSO/object files dsos
-S syms only consider symbols syms
-s key sort data by key key (such as PID)
--stdio use stdio interface
-U only display entries that resolve to symbol
-D dump raw trace data
input file defaults to perf.data
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1874
Perf Report Example
#
# ( For a higher level overview , try : perf report -- sort comm , dso )
#
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1875
Perf Script Command
read Perf data (created by Perf record) and display trace output
command line interface has following form:
perf script [options]
some common options include:
Option Description
-i file set input file to file
--pid pid only show events for process ID pid
--tid tid only show events for thread ID tid
-C cpu only show events for CPU cpu
input file defaults to perf.data
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1876
Perf Script Example
$ perf record -g -e cycles :u -F 13000 -o perf . data ./ array_sum
1
1
$ perf script -i perf . data
array_sum 15602 2408817.214222: 1 cycles :u:
cf0 _start (/ usr / lib64 /ld -2.20. so )
array_sum 15602 2408817.214230: 1 cycles :u:
cf0 _start (/ usr / lib64 /ld -2.20. so )
array_sum 15602 2408817.214234: 2 cycles :u:
cf0 _start (/ usr / lib64 /ld -2.20. so )
array_sum 15602 2408817.214237: 7 cycles :u:
cf0 _start (/ usr / lib64 /ld -2.20. so )
array_sum 15602 2408817.214241: 25 cycles :u:
cf0 _start (/ usr / lib64 /ld -2.20. so )
array_sum 15602 2408817.214245: 88 cycles :u:
cf0 _start (/ usr / lib64 /ld -2.20. so )
array_sum 15602 2408817.214248: 308 cycles :u:
cf0 _start (/ usr / lib64 /ld -2.20. so )
array_sum 15602 2408817.214253: 1081 cycles :u:
ffffffff8179bef0 page_fault ([ kernel . kallsyms ])
cf0 _start (/ usr / lib64 /ld -2.20. so )
array_sum 15602 2408817.214270: 3147 cycles :u:
4980 _dl_start (/ usr / lib64 /ld -2.20. so )
array_sum 15602 2408817.214274: 4536 cycles :u:
4 b8f _dl_start (/ usr / lib64 /ld -2.20. so )
cf8 _dl_start_user (/ usr / lib64 /ld -2.20. so )
[text deleted]
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1877
Perf Annotate Command
read Perf data (created by Perf record) and display annotated code
command line interface has following form:
perf annotate [options]
some common options include:
Option Description
-i file set input file to file
-s sym annotate symbol sym
-d dsos only consider symbols in DSO/object files dsos
-v increase verbosity level
-l print matching source lines
-P do not shorten displayed pathnames
-k file set vmlinux pathname to file
--stdio use stdio interface
--no-source disable displaying of source code
input file defaults to perf.data
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1878
Perf Annotate Example
$ perf record -g -e cycles :u -F 13000 -o perf . data ./ array_sum
[ text deleted ]
$ perf annotate -i perf . data -s naive_sum -l -- stdio
[ text deleted ]
: double naive_sum ( const double a [][ N ]) {
0.00 : 400807: push % rbp
0.00 : 400808: mov %rsp ,% rbp
0.00 : 40080 b: lea 0 x4000 (% rdi ) ,% rcx
: double sum = 0.0;
0.00 : 400812: pxor % xmm0 ,% xmm0
0.00 : 400816: lea 0 x2000000 (% rdi ) ,% rdx
0.00 : 40081 d: mov %rdi ,% rax
: for ( int j = 0; j < N; ++ j) {
: for ( int i = 0; i < M; ++ i) {
: sum += a[i ][ j ];
0.00 : 400820: addsd (% rax ) ,% xmm0
array_sum . cpp :11 100.00 : 400824: add $0x4000 ,% rax
[ text deleted ]
: double naive_sum ( const double a [][ N ]) {
: double sum = 0.0;
: for ( int j = 0; j < N; ++ j) {
: for ( int i = 0; i < M; ++ i) {
0.00 : 40082 a: cmp %rdx ,% rax
0.00 : 40082 d: jne 400820 < naive_sum ( double const (*) [2048])+0 x19 >
0.00 : 40082 f: add $0x8 ,% rdi
[ text deleted ]
0.00 : 400833: cmp %rcx ,% rdi
0.00 : 400836: jne 400816 < naive_sum ( double const (*) [2048])+0 xf >
[ text deleted ]
: }
: }
: return sum ;
: }
0.00 : 400838: pop % rbp
0.00 : 400839: retq
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1879
Example: Source Code
1 #include <iostream>
2 #include <algorithm>
3
4 constexpr int M = 4096;
5 constexpr int N = 4096;
6
7 [[gnu::noinline]]
8 double naive_sum(const double a[][N]) {
9 double sum = 0.0;
10 for (int j = 0; j < N; ++j) {
11 for (int i = 0; i < M; ++i) {
12 sum += a[i][j];
13 }
14 }
15 return sum;
16 }
17
18 [[gnu::noinline]]
19 double improved_sum(const double a[][N]) {
20 double sum = 0.0;
21 for (int i = 0; i < M; ++i) {
22 for (int j = 0; j < N; ++j) {
23 sum += a[i][j];
24 }
25 }
26 return sum;
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1880
Example: Source Code (Continued)
29 int main() {
30 for (int i = 0; i < 16; ++i) {
31 static double a[M][N];
32 static double b[M][N];
33 std::fill_n(&a[0][0], M * N, 1.0 / (M * N));
34 std::fill_n(&b[0][0], M * N, 1.0 / (M * N));
35 std::cout << naive_sum(a) << ’ ’;
36 std::cout << improved_sum(b) << ’\n’;
37 }
38 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1881
Profile of Cycles
# To display the perf . data header info , please use -- header /-- header - only options .
#
# dso : array_sum
# Samples : 16 K of event ’ cycles :u ’
# Event count ( approx .): 14049539983
#
# Children Self Command Symbol
# ........ ........ ......... .....................
#
99.97% 0.00% array_sum [.] __libc_start_main
|
--- __libc_start_main
0 x46e258d4c544155
99.97% 10.92% array_sum [.] main
|
--- main
__libc_start_main
0 x46e258d4c544155
82.97% 82.97% array_sum [.] naive_sum
|
--- naive_sum
main
__libc_start_main
0 x46e258d4c544155
5.90% 5.90% array_sum [.] improved_sum
|
--- improved_sum
main
__libc_start_main
0 x46e258d4c544155
[ text deleted ]
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1882
Cycles for naive_sum
: 0000000000400807 < naive_sum ( double const (*) [4096]) >:
: _Z9naive_sumPA4096_Kd ():
[ text deleted ]
: [[ gnu :: noinline ]]
: double naive_sum ( const double a [][ N ]) {
0.00 : 400807: push % rbp
0.00 : 400808: mov %rsp ,% rbp
0.00 : 40080 b: lea 0 x8000 (% rdi ) ,% rcx
: double sum = 0.0;
0.00 : 400812: pxor % xmm0 ,% xmm0
0.00 : 400816: lea 0 x8000000 (% rdi ) ,% rdx
0.00 : 40081 d: mov %rdi ,% rax
: for ( int j = 0; j < N; ++ j) {
: for ( int i = 0; i < M; ++ i) {
: sum += a[i ][ j ];
0.00 : 400820: addsd (% rax ) ,% xmm0
array_sum . cpp :12 99.93 : 400824: add $0x8000 ,% rax
[ text deleted ]
: for ( int j = 0; j < N; ++ j) {
: for ( int i = 0; i < M; ++ i) {
0.07 : 40082 a: cmp %rdx ,% rax
0.00 : 40082 d: jne 400820 < naive_sum ( double const (*) [4096])+0 x19 >
0.00 : 40082 f: add $0x8 ,% rdi
[ text deleted ]
: [[ gnu :: noinline ]]
: double naive_sum ( const double a [][ N ]) {
: double sum = 0.0;
: for ( int j = 0; j < N; ++ j) {
0.00 : 400833: cmp %rcx ,% rdi
0.00 : 400836: jne 400816 < naive_sum ( double const (*) [4096])+0 xf >
: for ( int i = 0; i < M; ++ i) {
: sum += a[i ][ j ];
: }
: }
: return sum ;
: }
0.00 : 400838: pop % rbp
0.00 : 400839: retq
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1883
Cycles for improved_sum
: 000000000040083 a < improved_sum ( double const (*) [4096]) >:
: _Z12improved_sumPA4096_Kd ():
[ text deleted ]
: [[ gnu :: noinline ]]
: double improved_sum ( const double a [][ N ]) {
0.00 : 40083 a: push % rbp
0.00 : 40083 b: mov %rsp ,% rbp
0.00 : 40083 e: lea 0 x8000000 (% rdi ) ,% rdx
: double sum = 0.0;
0.00 : 400845: pxor % xmm0 ,% xmm0
0.00 : 400849: lea 0 x8000 (% rdi ) ,% rax
: for ( int i = 0; i < M; ++ i) {
: for ( int j = 0; j < N; ++ j) {
: sum += a[i ][ j ];
0.00 : 400850: addsd (% rdi ) ,% xmm0
array_sum . cpp :23 99.70 : 400854: add $0x8 ,% rdi
[ text deleted ]
: for ( int i = 0; i < M; ++ i) {
: for ( int j = 0; j < N; ++ j) {
0.30 : 400858: cmp %rax ,% rdi
0.00 : 40085 b: jne 400850 < improved_sum ( double const (*) [4096])+0 x16 >
: }
:
: [[ gnu :: noinline ]]
: double improved_sum ( const double a [][ N ]) {
: double sum = 0.0;
: for ( int i = 0; i < M; ++ i) {
0.00 : 40085 d: cmp %rdx ,% rdi
0.00 : 400860: jne 400849 < improved_sum ( double const (*) [4096])+0 xf >
: for ( int j = 0; j < N; ++ j) {
: sum += a[i ][ j ];
: }
: }
: return sum ;
: }
0.00 : 400862: pop % rbp
0.00 : 400863: retq
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1884
Profile of Cache Misses
# To display the perf . data header info , please use -- header /-- header - only options .
#
# dso : array_sum
# Samples : 25 K of event ’cache - misses :u ’
# Event count ( approx .): 256620000
#
# Children Self Command Symbol
# ........ ........ ......... .....................
#
99.99% 0.00% array_sum [.] __libc_start_main
|
--- __libc_start_main
0 x46e258d4c544155
99.99% 3.67% array_sum [.] main
|
--- main
__libc_start_main
0 x46e258d4c544155
93.74% 93.73% array_sum [.] naive_sum
|
--- naive_sum
main
__libc_start_main
0 x46e258d4c544155
2.58% 2.58% array_sum [.] improved_sum
|
--- improved_sum
main
__libc_start_main
0 x46e258d4c544155
[ text deleted ]
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1885
Cache Misses for naive_sum
: 0000000000400807 < naive_sum ( double const (*) [4096]) >:
: _Z9naive_sumPA4096_Kd ():
[ text deleted ]
: [[ gnu :: noinline ]]
: double naive_sum ( const double a [][ N ]) {
0.00 : 400807: push % rbp
0.00 : 400808: mov %rsp ,% rbp
0.00 : 40080 b: lea 0 x8000 (% rdi ) ,% rcx
: double sum = 0.0;
0.00 : 400812: pxor % xmm0 ,% xmm0
0.00 : 400816: lea 0 x8000000 (% rdi ) ,% rdx
0.00 : 40081 d: mov %rdi ,% rax
: for ( int j = 0; j < N; ++ j) {
: for ( int i = 0; i < M; ++ i) {
: sum += a[i ][ j ];
0.00 : 400820: addsd (% rax ) ,% xmm0
array_sum . cpp :12 99.93 : 400824: add $0x8000 ,% rax
[ text deleted ]
: for ( int j = 0; j < N; ++ j) {
: for ( int i = 0; i < M; ++ i) {
0.07 : 40082 a: cmp %rdx ,% rax
0.00 : 40082 d: jne 400820 < naive_sum ( double const (*) [4096])+0 x19 >
0.00 : 40082 f: add $0x8 ,% rdi
[ text deleted ]
: [[ gnu :: noinline ]]
: double naive_sum ( const double a [][ N ]) {
: double sum = 0.0;
: for ( int j = 0; j < N; ++ j) {
0.00 : 400833: cmp %rcx ,% rdi
0.00 : 400836: jne 400816 < naive_sum ( double const (*) [4096])+0 xf >
: for ( int i = 0; i < M; ++ i) {
: sum += a[i ][ j ];
: }
: }
: return sum ;
: }
0.00 : 400838: pop % rbp
0.00 : 400839: retq
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1886
Cache Misses for improved_sum
: 000000000040083 a < improved_sum ( double const (*) [4096]) >:
: _Z12improved_sumPA4096_Kd ():
[ text deleted ]
: [[ gnu :: noinline ]]
: double improved_sum ( const double a [][ N ]) {
0.00 : 40083 a: push % rbp
0.00 : 40083 b: mov %rsp ,% rbp
0.00 : 40083 e: lea 0 x8000000 (% rdi ) ,% rdx
: double sum = 0.0;
0.00 : 400845: pxor % xmm0 ,% xmm0
0.00 : 400849: lea 0 x8000 (% rdi ) ,% rax
: for ( int i = 0; i < M; ++ i) {
: for ( int j = 0; j < N; ++ j) {
: sum += a[i ][ j ];
0.00 : 400850: addsd (% rdi ) ,% xmm0
array_sum . cpp :23 99.70 : 400854: add $0x8 ,% rdi
[ text deleted ]
: for ( int i = 0; i < M; ++ i) {
: for ( int j = 0; j < N; ++ j) {
0.30 : 400858: cmp %rax ,% rdi
0.00 : 40085 b: jne 400850 < improved_sum ( double const (*) [4096])+0 x16 >
: }
:
: [[ gnu :: noinline ]]
: double improved_sum ( const double a [][ N ]) {
: double sum = 0.0;
: for ( int i = 0; i < M; ++ i) {
0.00 : 40085 d: cmp %rdx ,% rdi
0.00 : 400860: jne 400849 < improved_sum ( double const (*) [4096])+0 xf >
: for ( int j = 0; j < N; ++ j) {
: sum += a[i ][ j ];
: }
: }
: return sum ;
: }
0.00 : 400862: pop % rbp
0.00 : 400863: retq
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1887
Additional Remarks
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1888
Flame Graphs
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1889
Flame Graph Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1890
Generating Flame Graphs
can generate flamegraphs from Perf data by using software available from
https://github.com/brendangregg/FlameGraph
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1891
Section 8.1.1
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1892
Talks I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1893
References I
1 B. Gregg The Flame Graph, ACM Queue, March 2016, pages 1–28.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1894
Section 8.2
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1895
Motivation
often easy to identify in general terms which parts of code are slow
sometimes more difficult to pinpoint precise reason why code is slow (i.e.,
what is precise cause of bottleneck)
often need to consider factors such as:
cache behavior
memory and resource contention
floating-point efficiency
branch behavior
often, processor itself in best position to provide information related to
above factors
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1896
Hardware Performance Counters
hardware performance counters are specialized registers used to
measure various aspects of processor performance
hardware counters can provide insight into:
timing
cache behaviors (e.g., cache misses and cache coherence protocol events)
branch behaviors (e.g., incorrect branch predictions)
pipeline behavior (e.g., stalls)
memory and resource access patterns
floating-point efficiency
instructions per cycle
hardware counter information can be obtained with:
subroutine or basic block resolution
process or thread attribution
provide low-level information that often cannot be obtained easily through
other means
useful for performance analysis and tuning (e.g., identifying bottlenecks in
code)
use of hardware performance counters has no or little overhead
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1897
Performance API (PAPI) Software
Performance API (PAPI) software provides portable and efficient API for
accessing hardware performance counters found on modern processors
more generally allows monitoring of system information on range of
components, such as CPUs, network interface cards, and power monitors
consists of library and several utility programs
open source
written in C
supports most mainstream Unix-based operating systems (e.g., Linux, OS
X, and other Unix variants); older versions support Microsoft Windows
supports most modern processors (e.g., Intel and AMD 32- and 64-bit
x86, ARM, MIPS, Intel Itanium II, UltraSparc I, II, and III, and IBM Power
4, 5, 6, and 7)
web site: http://icl.utk.edu/papi
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1898
Events
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1899
Events (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1900
PAPI
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1901
PAPI High-Level Interface
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1902
Functions in PAPI High-Level Interface
Function Description
PAPI_accum_counters add current counts to array and reset counters
PAPI_flips get floating-point instruction rate and real and proces-
sor time
PAPI_flops get floating-point operation rate and real and processor
time
PAPI_ipc get instructions per cycle and real and processor time
PAPI_num_counters get number of hardware counters available on system
PAPI_read_counters copy current counts to array and reset counters
PAPI_start_counters start counting hardware events
PAPI_stop_counters stop counters and return current counts
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1903
Some Commonly-Used Preset Events
Instruction Mix
Name Description
PAPI_LD_INS number of load instructions
PAPI_SR_INS number of store instructions
PAPI_LST_INS number of load/store instructions
PAPI_BR_INS number of branch instructions
PAPI_INT_INS number of integer instructions
PAPI_FP_INS number of floating-point instructions
PAPI_VEC_INS number of vector/SIMD instructions
PAPI_VEC_SP number of single-precision vector/SIMD instructions
PAPI_VEC_DP number of double-precision vector/SIMD instructions
PAPI_TOT_INS number of instructions in total
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1904
Some Commonly-Used Preset Events (Continued 1)
Clock Cycles
Name Description
PAPI_TOT_CYC total number of clock cycles
FLOPS
Name Description
PAPI_FP_OPS number of floating-point operations
PAPI_SP_OPS number of floating-point operations executed, optimized to count
scaled single-precision vector operations
PAPI_DP_OPS number of floating-point operations executed, optimized to count
scaled double-precision vector operations
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1905
Some Commonly-Used Preset Events (Continued 2)
L1 Cache Behavior
Name Description
PAPI_L1_DCA number of L1 data cache accesses
PAPI_L1_DCH number of L1 data cache hits
PAPI_L1_DCM number of L1 data cache misses
PAPI_L1_DCR number of L1 data cache reads
PAPI_L1_DCW number of L1 data cache writes
PAPI_L1_ICA number of L1 instruction cache accesses
PAPI_L1_ICH number of L1 instruction cache hits
PAPI_L1_ICM number of L1 instruction cache misses
PAPI_L1_ICR number of L1 instruction cache reads
PAPI_L1_ICW number of L1 instruction cache writes
PAPI_L1_LDM number of L1 load misses
PAPI_L1_STM number of L1 store misses
PAPI_L1_TCA number of L1 cache accesses (in total)
PAPI_L1_TCH number of L1 cache hits (in total)
PAPI_L1_TCM number of L1 cache misses (in total)
PAPI_L1_TCR number of L1 cache reads (in total)
PAPI_L1_TCW number of L1 cache writes (in total)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1906
Some Commonly-Used Preset Events (Continued 3)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1907
Event Usage Examples
most frequently used events are often those related to cache behavior
instructions per cycle could be computed from events:
PAPI_TOT_CYC and PAPI_TOT_INS
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1908
Code Example Using PAPI High-Level Interface
1 #include <iostream>
2 #include <papi.h>
3
4 void do_work() {for (volatile auto i = 1’000’000; i > 0; --i) {}}
5
6 int main() {
7 constexpr int num_events = 2;
8 int events[num_events] = {PAPI_TOT_INS, PAPI_TOT_CYC};
9 long long values[num_events];
10 if (PAPI_start_counters(events, num_events) != PAPI_OK)
11 {std::cerr << "cannot start counters\n"; return 1;}
12 do_work();
13 if (PAPI_stop_counters(values, num_events) != PAPI_OK)
14 {std::cerr << "cannot stop counters\n"; return 1;}
15 for (auto i : values) {std::cout << i << ’\n’;}
16 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1909
PAPI Low-Level Interface
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1910
Some Functions in PAPI Low-Level Interface
Function Description
PAPI_library_init initialize PAPI library
PAPI_shutdown cleanup PAPI library
PAPI_create_eventset create event set
PAPI_destroy_eventset destroys empty event set
PAPI_cleanup_eventset removes all events from event set
PAPI_add_event add preset or native hardware event to event set
PAPI_add_events add multiple preset or native hardware events to
event set
PAPI_start start counting hardware events in event set
PAPI_read read hardware counters from event set
PAPI_reset reset hardware event counts in event set
PAPI_accum adds hardware counters from event set to elements
in array and resets counters
PAPI_stop stop counting hardware events in event set
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1911
Code Example Using PAPI Low-Level Interface
1 #include <iostream>
2 #include <papi.h>
3
4 void do_work() {for (volatile auto i = 1’000’000; i > 0; --i) {}}
5
6 int main() {
7 constexpr int num_events = 2;
8 int event_set = PAPI_NULL;
9 int events[num_events] = {PAPI_TOT_INS, PAPI_TOT_CYC};
10 long long values[num_events];
11 if (PAPI_library_init(PAPI_VER_CURRENT) != PAPI_VER_CURRENT)
12 {std::cerr << "cannot initialize\n"; return 1;}
13 if (PAPI_create_eventset(&event_set) != PAPI_OK)
14 {std::cerr << "cannot create event set\n"; return 1;}
15 if (PAPI_add_events(event_set, events, num_events) != PAPI_OK)
16 {std::cerr << "cannot add events\n"; return 1;}
17 if (PAPI_start(event_set) != PAPI_OK)
18 {std::cerr << "cannot start\n"; return 1;}
19 do_work();
20 if (PAPI_stop(event_set, values) != PAPI_OK)
21 {std::cerr << "cannot stop\n"; return 1;}
22 if (PAPI_cleanup_eventset(event_set) != PAPI_OK)
23 {std::cerr << "cannot cleanup event set\n"; return 1;}
24 if (PAPI_destroy_eventset(&event_set) != PAPI_OK)
25 {std::cerr << "cannot destroy event set\n"; return 1;}
26 for (auto i : values) {std::cout << i << ’\n’;}
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1912
PAPI Utilities
Name Description
papi_avail provides availability and detail information for PAPI pre-
set events
papi_clockres measures and reports clock latency and resolution for
PAPI timers
papi_cost computes execution time costs for basic PAPI opera-
tions
papi_command_line executes PAPI preset or native events from command
line
papi_decode provides availability and detail information for PAPI pre-
set events
papi_event_chooser given list of named events, lists other events that can
be counted with them
papi_mem_info provides information on memory architecture of current
processor
papi_native_avail provides detailed information for PAPI native events
papi_version provides version information for PAPI
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1913
Example papi_avail Output
Available events and hardware information .
--------------------------------------------------------------------------------
PAPI Version : 5.3.2.0
Vendor string and code : GenuineIntel (1)
Model string and code : Intel (R) Core ( TM ) i7 -3820 QM CPU @ 2.70 GHz (58)
CPU Revision : 9.000000
CPUID Info : Family : 6 Model : 58 Stepping : 9
CPU Max Megahertz : 3700
CPU Min Megahertz : 1200
Hdw Threads per core : 2
Cores per Socket : 4
Sockets : 1
NUMA Nodes : 1
CPUs per Node : 8
Total CPUs : 8
Running in a VM : no
Number Hardware Counters : 11
Max Multiplex Counters : 64
--------------------------------------------------------------------------------
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1914
Example papi_mem_info Output
Memory Cache and TLB Hierarchy Information .
------------------------------------------------------------------------
TLB Information .
There may be multiple descriptors for each level of TLB
if multiple page sizes are supported .
L1 Data TLB :
Page Size : 4 KB
Number of Entries : 64
Associativity : 4
[ other TLB information deleted ]
Cache Information .
L1 Data Cache :
Total size : 32 KB
Line size : 64 B
Number of Lines : 512
Associativity : 8
L1 Instruction Cache :
Total size : 32 KB
Line size : 64 B
Number of Lines : 512
Associativity : 8
L2 Unified Cache :
Total size : 256 KB
Line size : 64 B
Number of Lines : 4096
Associativity : 8
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1915
Example papi_native_avail Output
Available native events and hardware information .
--------------------------------------------------------------------------------
PAPI Version : 5.3.2.0
Vendor string and code : GenuineIntel (1)
Model string and code : Intel (R) Core ( TM ) i7 -3820 QM CPU @ 2.70 GHz (58)
CPU Revision : 9.000000
CPUID Info : Family : 6 Model : 58 Stepping : 9
CPU Max Megahertz : 3700
CPU Min Megahertz : 1200
Hdw Threads per core : 2
Cores per Socket : 4
Sockets : 1
NUMA Nodes : 1
CPUs per Node : 8
Total CPUs : 8
Running in a VM : no
Number Hardware Counters : 11
Max Multiplex Counters : 64
--------------------------------------------------------------------------------
===============================================================================
Native Events in Component : perf_event
===============================================================================
[ lines deleted ]
--------------------------------------------------------------------------------
| perf :: L1 - DCACHE - LOADS |
| L1 cache load accesses |
--------------------------------------------------------------------------------
[ lines deleted ]
===============================================================================
Native Events in Component : coretemp
===============================================================================
| coretemp ::: hwmon0 : temp1_input |
| degrees C , acpitz module , label ? |
--------------------------------------------------------------------------------
[ lines deleted ]
Total events reported : 322
native_avail .c PASSED
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1916
Section 8.2.1
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1917
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1918
Section 8.3
Gprof
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1919
Gprof
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1920
Comments on Gprof
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1922
Using Gprof
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1923
Gprof Output
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1924
Example: Source Code
1 #include <algorithm>
2
3 constexpr int M = 1024;
4 constexpr int N = 1024;
5 constexpr int P = 1024;
6
7 // c += a * b
8 void naive_matmul(const double a[][N], const double b[][P],
9 double c[][P]) {
10 for (int i = 0; i < M; ++i) {
11 for (int j = 0; j < N; ++j) {
12 for (int k = 0; k < P; ++k)
13 {c[i][j] += a[i][k] * b[k][j];}
14 }
15 }
16 }
17
18 // c += a * b
19 void improved_matmul(const double a[][N], const double b[][P],
20 double c[][P]) {
21 for (int i = 0; i < M; ++i) {
22 for (int k = 0; k < P; ++k) {
23 for (int j = 0; j < N; ++j)
24 {c[i][j] += a[i][k] * b[k][j];}
25 }
26 }
27 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1925
Example: Source Code (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1926
Flat Profile Information
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1927
Example: Flat Profile
Flat profile :
Each sample counts as 0.01 seconds .
% cumulative self self total
time seconds seconds calls s/ call s/ call name
89.48 7.55 7.55 1 7.55 7.55 naive_matmul (
double const (*) [1024] ,
double const (*) [1024] ,
double (*) [1024])
11.23 8.50 0.95 1 0.95 0.95 improved_matmul (
double const (*) [1024] ,
double const (*) [1024] ,
double (*) [1024])
0.12 8.51 0.01 main
0.00 8.51 0.00 1 0.00 0.00
_GLOBAL__sub_I__Z12naive_matmulPA1024_KdS1_PA1024_d
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1928
Call Graph Information
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1929
Call Graph Information (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1930
Example: Call Graph
Call graph
granularity : each sample hit covers 2 byte (s) for 0.12% of 8.51 seconds
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1931
Section 8.3.1
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1932
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1933
Section 8.4
Valgrind/Callgrind
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1934
Valgrind
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1935
Callgrind
collects function call graph information and measures number of
instructions executed and cache behavior for program
does not measure execution time per se; but provides sufficient
information to make clock cycle estimates (as is done in KCachegrind)
can be used to determine cache hit/miss counts and miss rate on program
wide, per function, and per source-code line basis
simulates L1 instruction/data cache and L2 cache
parameters for each cache can be specified (i.e., size, associativity, and
line size) but default to values taken from machine’s cache
simplistic cache model only approximates real cache
handles code in shared libraries
typically 15 to 100 times slower (depending on whether cache and branch
simulation enabled)
does not fully support IEEE 754 (floating-point arithmetic standard)
consequently, code that uses floating-point arithmetic in particular ways
may not behave correctly
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1936
Support for Floating-Point Arithmetic in Valgrind
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1938
The callgrind_annotate Command
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1939
Using Callgrind
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1940
Example: Source Code
1 #include <iostream>
2 #include <algorithm>
3
4 constexpr int M = 2048;
5 constexpr int N = 2048;
6
7 double naive_sum(const double a[][N]) {
8 double sum = 0.0;
9 for (int j = 0; j < N; ++j) {
10 for (int i = 0; i < M; ++i)
11 {sum += a[i][j];}
12 }
13 return sum;
14 }
15
16 double improved_sum(const double a[][N]) {
17 double sum = 0.0;
18 for (int i = 0; i < M; ++i) {
19 for (int j = 0; j < N; ++j)
20 {sum += a[i][j];}
21 }
22 return sum;
23 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1941
Example: Source Code (Continued)
25 int main() {
26 static double a[M][N];
27 std::fill_n(&a[0][0], M * N, 1.0 / (M * N));
28 std::cout << naive_sum(a) << ’\n’;
29 static double b[M][N];
30 std::fill_n(&b[0][0], M * N, 1.0 / (M * N));
31 std::cout << improved_sum(b) << ’\n’;
32 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1942
Example: Callgrind
$ valgrind -- tool = callgrind --cache - sim = yes -- branch - sim = yes --log - file = callgrind . log --
callgrind -out - file = callgrind . out ./ array_sum
$ cat callgrind . log
==23469== Callgrind , a call - graph generating cache profiler
==23469== Copyright (C) 2002 -2013 , and GNU GPL ’d , by Josef Weidendorfer et al .
==23469== Using Valgrind -3.10.1 and LibVEX ; rerun with -h for copyright info
==23469== Command : ./ array_sum
==23469== Parent PID : 23449
==23469==
- -23469 - - warning : L3 cache found , using its data for the LL simulation .
==23469== For interactive control , run ’ callgrind_control -h ’.
==23469==
==23469== Events : Ir Dr Dw I1mr D1mr D1mw ILmr DLmr DLmw Bc Bcm Bi Bim
==23469== Collected : 70339139 9142838 8663373 1601 4738282 1051026 1585 4728422 1050172
17247597 30398 4923 423
==23469==
==23469== I refs : 70 ,339 ,139
==23469== I1 misses : 1 ,601
==23469== LLi misses : 1 ,585
==23469== I1 miss rate : 0.0%
==23469== LLi miss rate : 0.0%
==23469==
==23469== D refs : 17 ,806 ,211 ( 9 ,142 ,838 rd + 8 ,663 ,373 wr )
==23469== D1 misses : 5 ,789 ,308 ( 4 ,738 ,282 rd + 1 ,051 ,026 wr )
==23469== LLd misses : 5 ,778 ,594 ( 4 ,728 ,422 rd + 1 ,050 ,172 wr )
==23469== D1 miss rate : 32.5% ( 51.8% + 12.1% )
==23469== LLd miss rate : 32.4% ( 51.7% + 12.1% )
==23469==
==23469== LL refs : 5 ,790 ,909 ( 4 ,739 ,883 rd + 1 ,051 ,026 wr )
==23469== LL misses : 5 ,780 ,179 ( 4 ,730 ,007 rd + 1 ,050 ,172 wr )
==23469== LL miss rate : 6.5% ( 5.9% + 12.1% )
==23469==
==23469== Branches : 17 ,252 ,520 (17 ,247 ,597 cond + 4 ,923 ind )
==23469== Mispredicts : 30 ,821 ( 30 ,398 cond + 423 ind )
==23469== Mispred rate : 0.1% ( 0.1% + 8.5% )
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1943
Example: callgrind_annotate
[text deleted]
--------------------------------------------------------------------------------
-- User - annotated source : array_sum . cpp
--------------------------------------------------------------------------------
Ir Dr Dw I1mr D1mr D1mw ILmr DLmr DLmw Bc Bcm Bi Bim
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1944
KCachegrind
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1945
KCachegrind Example: Source/Assembly Annotation
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1946
KCachegrind Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1947
Section 8.4.1
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1948
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1949
Part 9
Build Tools
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1950
Section 9.1
Build Tools
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1951
Build Tools
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1952
Examples of Build Tools
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1953
Section 9.2
Make
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1954
Make
make command
controls generation of executables and/or other non-source files from
program’s source files
extremely popular tool for automating build process
available on many platforms (e.g., Unix, Microsoft Windows, Mac OS X);
used extensively on Unix systems
very flexible
can handle building multiple programs consisting of hundreds of source
files or single program consisting of only one source file
can be used to build almost anything (i.e., need not be a program)
for example, all materials for this course typeset using LATEX (e.g.,
coursepack, slides, handouts, exams), and make utility used to compile
LATEX source code into PDF documents
one of most popular implementations of make is GNU Make
GNU Make web site: http://www.gnu.org/software/make
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1955
The make Command
target is something that can be built, typically (but not necessarily) file
such as executable file or object file
make command driven by data file called makefile
makefile usually named Makefile or makefile
command-line usage:
make [options] [targets]
targets: zero or more targets to be built
options: zero or more options
by default, looks for makefile called makefile and then Makefile
if no targets are specified, will build first target specified in makefile
only builds files that are out of date
most common command-line options include:
-n show commands that would be executed but do not actu-
ally execute them
-f makefile use makefile makefile
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1956
Makefiles
comment starts at hash character (i.e., “#”) and continues until end of line;
example:
# This comment continues until the end of the line.
supports variables
some important variables used by built-in rules:
Name Description
CXX C++ compiler command
CXXFLAGS C++ compiler options
LDFLAGS linker options
to assign value to variable, use equal sign; example:
CXX = g++
to substitute value of variable, use dollar sign followed by variable name in
parentheses; example:
$(CXX)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1957
Makefiles (Continued 1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1958
Makefiles (Continued 2)
normally, each target associated with file of same name (and building
target will create this file)
phony target: target that is not associated with any file
to identify target as phony make it prerequisite of special target called
“.PHONY”; example (specify all as phony target):
.PHONY: all
some special built-in variables that can be used in rules:
Name Description
$@ target
$< name of first prerequisite
$ˆ names of all of prerequisites separated by spaces
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1959
Makefile for hello Program
1 CXX = g++ # The C++ compiler command.
2 CXXFLAGS = -g -O # The C++ compiler options.
3 LDFLAGS = # The linker options (if any).
4
5 # The all target builds all of the programs handled by
6 # the makefile.
7 # This target has the dependency chain:
8 # all -> hello -> hello.o -> hello.cpp
9 all: hello
10
11 # The clean target removes all of the executable files
12 # and object files produced by the build process.
13 clean:
14 rm -f hello *.o
15
16 # The hello target builds the hello executable.
17 hello: hello.o
18 $(CXX) $(CXXFLAGS) -o $@ $ˆ $(LDFLAGS)
19
20 # Indicate that the all and clean targets do not
21 # correspond to actual files.
22 .PHONY: all clean
23
24 # The following rule is effectively built into make and
25 # therefore need not be explicitly specified:
26 # hello.o: hello.cpp
27 # $(CXX) $(CXXFLAGS) -c $<
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1960
Commentary on Makefile for hello Program
all target: builds all of the programs handled by the makefile (e.g.,
hello)
clean target: removes all of the executable files and object files produced
by build process (e.g., hello, hello.o)
although all and clean have no special meaning to make, very common
practice to provide targets with these particular names in all makefiles
hello target: compiles and links the hello program
chain of dependencies for all target:
all → hello → hello.o → hello.cpp
all and clean examples of phony targets
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1961
Section 9.2.1
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1962
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1963
Section 9.3
CMake
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1964
CMake
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1965
Users of CMake
CMake has very large user base and is employed in many open-source
and commercial projects
some users of CMake include:
Blender (https://www.blender.org)
Clang (http://clang.llvm.org)
Computational Geometry Algorithms Library (CGAL) (http://www.cgal.
org)
JasPer Image Processing/Coding Tool Kit (http://www.ece.uvic.ca/
˜mdadams/jasper)
K Desktop Environment (KDE) (https://www.kde.org)
MySQL (https://www.mysql.com)
Netflix (https://www.netflix.com)
OpenCV (http://opencv.org)
Qt (https://www.qt.io)
Second Life (http://secondlife.com)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1966
Build Process
Native
CMake CMake Native Build Tool
Built Code
Build Files Build Files
CMake build files: files used by CMake to describe build process for
software project (i.e., CMakeLists.txt and other build files it references)
native build tool: program used to build code for particular software
development environment being employed (e.g., make for Unix, MSBuild
for Microsoft Visual Studio, and xcodebuild for Apple Xcode)
native build files: files used by native build tool to control build process
(e.g., makefiles for Unix, project/solution files for Microsoft Visual Studio,
and project files for Apple Xcode)
build process consists of two steps:
1 CMake used, with CMake build files as input, to produce native build files
2 native build tool invoked to build code using native build files generated by
CMake
strictly speaking, CMake does not itself build code, but rather produces
build files that can be used by native build tool to build code
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1967
CMake Basics
To generate build files for a native build tool, use a command of the form:
cmake [options] [$source_directory]
The binary directory is assumed to be the current directory.
The source directory $source_dir defaults to the current directory
(resulting in an in-source build).
Some options include:
Option Description
-D var=val Set the CMake variable var to value val.
-G gen Set the build-system generator to gen.
--version Print name/version banner and exit.
--help Print usage information and exit.
--debug-output Enable debugging output.
--trace Enable tracing output.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1970
The cmake Command (Continued 1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1972
Hello World Example
source directory $SOURCE_DIR contains two files:
CMakeLists.txt and hello.cpp
commands to build with binary directory $BINARY_DIR:
cd $BINARY_DIR
cmake $SOURCE_DIR
cmake --build .
$SOURCE_DIR/hello.cpp
1 #include <iostream>
2 int main() {std::cout << "Hello, World!\n";}
$SOURCE_DIR/CMakeLists.txt
1 # Specify minimum required version of CMake.
2 cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
3
4 # Specify project and identify languages used.
5 project(hello LANGUAGES CXX)
6
7 # Add program target called hello.
8 add_executable(hello hello.cpp)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1973
Section 9.3.1
CMakeLists Files
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1974
Projects, Targets, and Build Configurations
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1975
Comments and Commands
comment starts with hash character (i.e., “#”) and continues until end of
line
file consists of sequence of commands
command consists of following (in order):
1 command name
2 opening parenthesis
3 whitespace-separated arguments
4 closing parenthesis
command example:
cmake_minimum_required(VERSION 3.1)
command names are case insensitive
anything in double quotes treated as single argument; for example, as in:
message("Hello World")
backslash character can be used to escape character such as double
quote; for example, as in:
message("\${X} is not a variable expansion")
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1976
Variables
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1977
Modules
module is file containing re-usable piece of CMakeLists code
normally use “.cmake” file name extension
most modules can be classified into one of following categories:
find
system introspection
utility
find module:
determines location of software elements such as header files and libraries
often module name starts with prefix “Find”
examples: FindBoost and FindOpenGL
system introspection module:
provides information about target system or compiler (e.g., size of various
types, availability of header files, compiler version)
often module name starts with prefix “Test” or “Check”
examples: CheckCXXSourceCompiles and CheckIncludeFile
utility module:
provides additional functions/macros for convenience
example: ExternalProject
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1978
Modules (Continued 1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1979
Commonly-Used Variables
Source and binary directories:
CMAKE_BINARY_DIR. The full path to the top-level directory of the current
C++ compiler:
CMAKE_CXX_COMPILER_ID. The C++ compiler in use (e.g., Clang, GNU,
Intel, MSVC).
CMAKE_CXX_STANDARD. Used to initialize the CXX_STANDARD property on all
targets, which selects version of C++ standard (e.g., 98, 11, and 14).
CMAKE_CXX_STANDARD_REQUIRED. Used to initialize the
CXX_STANDARD_REQUIRED property of all targets. This property determines
whether the specified version of C++ standard is required.
CMAKE_CXX_COMPILER. The compiler command used for C++ source code.
CMAKE_CXX_FLAGS. The compiler flags for compiling C++ source code.
CMAKE_CXX_FLAGS_DEBUG. The compiler flags for compiling C++ source
code for a debug build.
CMAKE_CXX_FLAGS_RELEASE. The compiler flags for compiling C++ source
code for a release build.
CMAKE_CXX_FLAGS_RELWITHDEBINFO. The compiler flags for compiling
C++ source code for a release build with debug flags.
CMAKE_CXX_FLAGS_MINSIZEREL. The compiler flags for compiling C++
source code for a release build with minimum code size.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1981
Commonly-Used Variables (Continued 2)
Linker:
CMAKE_EXE_LINKER_FLAGS. The linker flags used to create executables.
Windows, Darwin).
UNIX. Specifies if the target system’s OS is UNIX (or UNIX-like).
APPLE. Specifies if the target system’s OS is Mac OS X.
WIN32. Specifies if the target system’s OS is Microsoft Windows (32- or
64-bit).
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1982
Commonly-Used Variables (Continued 3)
Makefile builds:
CMAKE_VERBOSE_MAKEFILE. Enable verbose output from Makefile builds.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1983
Commonly-Used Commands
Initialization:
cmake_minimum_required. Set the minimum required version of Cmake
for a project.
cmake_policy. Manage CMake policy settings. (This is used to select
between old and new behaviors in CMake.)
project. Set a name, version, and enable languages for the entire project.
(If no languages specified, defaults to C and C++.)
option. Provide an option that the user can (optionally) select.
Adding targets:
add_executable. Add a program target.
add_library. Add a library target.
add_test. Add a test target. (This is used in conjunction with the module
CTest.)
add_custom_target. Add a target with no output file that is always out of
date.
add_custom_command. Add a custom build rule to the generated build
system.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1984
Commonly-Used Commands (Continued 1)
for linking a target. (May be used multiple times for the same target.)
set_target_properties. Set properties for a target. (Some properies
include: OUTPUT_NAME, SOVERSION, and VERSION.)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1985
Commonly-Used Commands (Continued 2)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1986
Commonly-Used Commands (Continued 3)
in a list.
while and endwhile. Evaluate a group of commands while a condition is
true.
function and endfunction. Record a function for later invocation as a
command.
macro and endmacro. Record a macro for later invocation as a command.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1987
Commonly-Used Commands (Continued 4)
Other:
message. Display a message to the user.
configure_file. Copy a file to another location and modify its contents.
install. Specify rules to run at install time (e.g., rules to install programs,
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1988
Commonly-Used Modules
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1989
Commonly-Used Modules (Continued 1)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1990
Some Find and Pkg-Config Modules
Boost
https://cmake.org/cmake/help/v3.10/module/FindBoost.html
variables: Boost_FOUND, Boost_INCLUDE_DIRS, Boost_LIBRARY_DIRS,
Boost_LIBRARIES
imported targets: Boost::boost, Boost::component
Doxygen
https://cmake.org/cmake/help/v3.10/module/FindDoxygen.html
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1991
Some Find and Pkg-Config Modules (Continued 1)
variables: CMAKE_THREAD_LIBS_INIT
imported targets: Threads::Threads
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1992
Using Per-Target Versus Global Settings
can set compiler options, compiler definitions, include directories, and link
libraries in two ways:
1 per target (e.g., using target_compile_options,
Examples
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1994
Hello World Example Revisited
hello.cpp
1 #include <iostream>
2
3 int main() {std::cout << "Hello, World!\n";}
CMakeLists.txt
1 # Specify minimum required version of CMake.
2 cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
3
4 # Specify project and identify languages used.
5 project(hello LANGUAGES CXX)
6
7 # Print message indicating detected OS.
8 if (UNIX)
9 set(platform "Unix")
10 elseif (WIN32)
11 set(platform "Microsoft Windows")
12 else()
13 set(platform "Unknown")
14 endif()
15 message("OS is ${platform}")
16
17 # Add program target called hello.
18 add_executable(hello hello.cpp)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1995
Test Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1996
Test Example: Source Code (Including Some Scripts)
hello.cpp
1 #include <iostream>
2
3 int main() {std::cout << "Hello, World!\n";}
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1997
Test Example: CMakeLists File
CMakeLists.txt
1 # Specify minimum required version of CMake.
2 cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
3
4 # Specify project and identify languages used.
5 project(hello LANGUAGES CXX)
6
7 # Include the CTest module for testing.
8 include(CTest)
9
10 # Find the Bourne shell.
11 find_program(sh SH_COMMAND)
12
13 # Add program target called hello.
14 add_executable(hello hello.cpp)
15
16 # Create a wrapper script that initializes the environment
17 # for any test scripts.
18 configure_file(${CMAKE_SOURCE_DIR}/test_wrapper.in
19 ${CMAKE_BINARY_DIR}/test_wrapper @ONLY)
20
21 # Add a test that invokes run_test via a wrapper script.
22 add_test(run_test ${SH_COMMAND}
23 ${CMAKE_BINARY_DIR}/test_wrapper
24 ${CMAKE_SOURCE_DIR}/run_test)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1998
Boost Log Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 1999
Boost Log Example: Source Code
main.cpp
1 #include <boost/log/trivial.hpp>
2
3 int main() {
4 BOOST_LOG_TRIVIAL(warning)
5 << "A warning severity message";
6 BOOST_LOG_TRIVIAL(error)
7 << "An error severity message";
8 BOOST_LOG_TRIVIAL(fatal)
9 << "A fatal severity message";
10 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2000
Boost Log Example: CMakeLists File [Without Imported Targets]
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
2 project(boost_example LANGUAGES CXX)
3
4 # Find the required libraries (i.e., POSIX threads and Boost).
5 set(Boost_USE_MULTITHREADED ON)
6 find_package(Threads REQUIRED)
7 find_package(Boost 1.54.0 REQUIRED COMPONENTS log)
8
9 # Define a program target called my_app.
10 add_executable(my_app main.cpp)
11
12 # Set the includes, defines, and libraries for the my_app target.
13 target_include_directories(my_app PUBLIC ${Boost_INCLUDE_DIRS})
14 target_compile_definitions(my_app PUBLIC "-DBOOST_LOG_DYN_LINK")
15 target_link_libraries(my_app ${Boost_LIBRARIES}
16 ${CMAKE_THREAD_LIBS_INIT})
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2001
Boost Log Example: CMakeLists File [With Imported Targets]
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
2 project(boost_example LANGUAGES CXX)
3
4 # Find the required libraries (i.e., POSIX threads and Boost).
5 set(Boost_USE_MULTITHREADED ON)
6 find_package(Threads REQUIRED)
7 find_package(Boost 1.54.0 REQUIRED COMPONENTS log)
8
9 # Define a program target called my_app.
10 add_executable(my_app main.cpp)
11
12 # Set the defines, includes, and libraries for the my_app target.
13 target_compile_definitions(my_app PUBLIC "-DBOOST_LOG_DYN_LINK")
14 target_link_libraries(my_app Boost::log Threads::Threads)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2002
OpenGL/GLFW Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2003
OpenGL/GLFW Example: Source Code
trivial.cpp
1 #include <cstdlib>
2 #include <GLFW/glfw3.h>
3
4 void display(GLFWwindow* window) {
5 glfwMakeContextCurrent(window);
6 glClearColor(0.0, 1.0, 1.0, 0.0);
7 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
8 glfwSwapBuffers(window);
9 }
10
11 int main(int argc, char** argv) {
12 if (!glfwInit()) {return EXIT_FAILURE;}
13 glfwSwapInterval(1);
14 GLFWwindow* window = glfwCreateWindow(512, 512, argv[0],
15 nullptr, nullptr);
16 if (!window) {
17 glfwTerminate();
18 return EXIT_FAILURE;
19 }
20 glfwSetWindowRefreshCallback(window, display);
21 while (!glfwWindowShouldClose(window))
22 {glfwWaitEvents();}
23 glfwTerminate();
24 return EXIT_SUCCESS;
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2004
OpenGL/GLFW Example: CMakeLists File [Without Imported Targets]
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
2 project(opengl_example LANGUAGES CXX)
3 set(CMAKE_CXX_STANDARD 11)
4 set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
5
6 # Find the required libraries (i.e., OpenGL, GLEW, and GLFW).
7 find_package(OpenGL REQUIRED)
8 find_package(GLEW REQUIRED)
9 find_package(PkgConfig REQUIRED)
10 pkg_search_module(GLFW REQUIRED glfw3)
11
12 # Define a program target called trivial.
13 add_executable(trivial trivial.cpp)
14
15 # Set the includes and libraries for the trivial target.
16 target_include_directories(trivial PUBLIC ${GLFW_INCLUDE_DIRS}
17 ${GLEW_INCLUDE_DIRS} ${OPENGL_INCLUDE_DIR})
18 target_link_libraries(trivial ${GLFW_LIBRARIES} ${GLEW_LIBRARIES}
19 ${OPENGL_LIBRARIES})
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2005
OpenGL/GLFW Example: CMakeLists File [With Imported Targets]
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
2 project(opengl_example LANGUAGES CXX)
3 set(CMAKE_CXX_STANDARD 11)
4 set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
5
6 # Find the required libraries (i.e., OpenGL, GLEW, and GLFW).
7 find_package(OpenGL REQUIRED)
8 find_package(GLEW REQUIRED)
9 find_package(PkgConfig REQUIRED)
10 pkg_search_module(GLFW REQUIRED glfw3)
11
12 # Define a program target called trivial.
13 add_executable(trivial trivial.cpp)
14
15 # Set the includes and libraries for the trivial target.
16 target_include_directories(trivial PUBLIC ${GLFW_INCLUDE_DIRS})
17 target_link_libraries(trivial ${GLFW_LIBRARIES} GLEW::GLEW
18 OpenGL::GL)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2006
OpenGL/GLUT Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2007
OpenGL/GLUT Example: Source Code
trivial.cpp
1 #include <GL/glut.h>
2
3 void display() {
4 glClearColor(0.0, 1.0, 1.0, 0.0);
5 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
6 glutSwapBuffers();
7 }
8
9 int main(int argc, char** argv) {
10 glutInit(&argc, argv);
11 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
12 glutInitWindowSize(512, 512);
13 glutCreateWindow(argv[0]);
14 glutDisplayFunc(display);
15 glutMainLoop();
16 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2008
OpenGL/GLUT Example: CMakeLists File [Without Imported Targets]
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
2 project(opengl_example LANGUAGES CXX)
3
4 # Find the required libraries (i.e., OpenGL and GLUT).
5 find_package(OpenGL REQUIRED)
6 find_package(GLUT REQUIRED)
7
8 # Define a program target called trivial.
9 add_executable(trivial trivial.cpp)
10
11 # Set the includes and libraries for the trivial target.
12 target_include_directories(trivial PUBLIC ${GLUT_INCLUDE_DIR}
13 ${OPENGL_INCLUDE_DIR})
14 target_link_libraries(trivial ${GLUT_LIBRARIES} ${OPENGL_LIBRARIES})
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2009
OpenGL/GLUT Example: CMakeLists File [With Imported Targets]
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
2 project(opengl_example LANGUAGES CXX)
3
4 # Find the required libraries (i.e., OpenGL and GLUT).
5 find_package(OpenGL REQUIRED)
6 find_package(GLUT REQUIRED)
7
8 # Define a program target called trivial.
9 add_executable(trivial trivial.cpp)
10
11 # Set the includes and libraries for the trivial target.
12 target_link_libraries(trivial GLUT::GLUT OpenGL::GL)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2010
CGAL Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2011
CGAL Example: Source Code
orient_test.cpp
1 #include <iostream>
2 #include <string>
3 #include <CGAL/Cartesian.h>
4 #include <CGAL/Filtered_kernel.h>
5
6 std::string toString(CGAL::Orientation orient) {
7 switch (orient) {
8 case CGAL::LEFT_TURN:
9 return "left turn";
10 case CGAL::RIGHT_TURN:
11 return "right turn";
12 case CGAL::COLLINEAR:
13 return "collinear";
14 }
15 }
16
17 int main(int argc, char** argv) {
18 using Point = CGAL::Point_2<CGAL::Filtered_kernel<
19 CGAL::Cartesian<double>>>;
20 Point a, b, q;
21 while (std::cin >> a >> b >> q) {
22 auto orient = CGAL::orientation(a, b, q);
23 std::cout << toString(orient) << ’\n’;
24 }
25 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2012
CGAL Example: CMakeLists File
CMakeLists.txt
1 # Specify minimum required version of CMake.
2 cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
3
4 # Specify project and enable the C++ language.
5 project(cgal_demo LANGUAGES CXX)
6
7 # Find the required CGAL package.
8 find_package(CGAL REQUIRED)
9
10 # On some systems, GCC may need the -frounding-math option.
11 if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
12 add_compile_options("-frounding-math")
13 endif()
14
15 # Add a program target called orient_test.
16 add_executable(orient_test orient_test.cpp)
17
18 # Specify the includes and libraries for the orient_test target.
19 target_include_directories(orient_test PUBLIC ${CGAL_INCLUDE_DIRS})
20 target_link_libraries(orient_test ${CGAL_LIBRARY} ${GMP_LIBRARIES})
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2013
HG2G Example: Overview
want to be able to build and install HG2G library and application that uses
library
code written in C++
files in project:
CMakeLists.txt
app/CMakeLists.txt
app/answer.cpp
hg2g/CMakeLists.txt
hg2g/answer.cpp
hg2g/question.cpp
hg2g/include/hg2g/answer.hpp
hg2g/include/hg2g/config.hpp.in
project has:
library target hg2g
executable target answer
option HG2G_ZAPHOD (which takes a boolean value)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2014
HG2G Example: Library Source Code
hg2g/include/hg2g/config.hpp.in
1 #ifndef HG2G_CONFIG_H
2 #define HG2G_CONFIG_H
3 #define HG2G_VERSION "@HG2G_VERSION@"
4 #cmakedefine HG2G_ZAPHOD
5 #endif
hg2g/include/hg2g/answer.hpp
1 #include <string>
2 namespace hg2g {
3 std::string answer_to_ultimate_question();
4 std::string ultimate_question();
5 }
hg2g/answer.cpp
1 #include "hg2g/answer.hpp"
2 namespace hg2g {
3 std::string answer_to_ultimate_question() {return "42";}
4 }
hg2g/question.cpp
1 #include "hg2g/answer.hpp"
2 namespace hg2g {
3 std::string ultimate_question() {throw 42;}
4 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2015
HG2G Example: Application Source Code
app/answer.cpp
1 #include <iostream>
2 #include <hg2g/config.hpp>
3 #include <hg2g/answer.hpp>
4
5 int main() {
6 #ifdef HG2G_ZAPHOD
7 std::cout << "HG2G_ZAPHOD is defined\n";
8 #endif
9 std::cout << "According to version " << HG2G_VERSION <<
10 " of the HG2G library:\n";
11 std::cout <<
12 "The answer to the ultimate question is " <<
13 hg2g::answer_to_ultimate_question() << ".\n";
14 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2016
HG2G Example: CMakeLists Files
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
2 project(hg2g_example LANGUAGES CXX)
3 option(HG2G_ZAPHOD "Define HG2G_ZAPHOD" FALSE)
4
5 # Set the version number and name.
6 set(HG2G_VERSION_MAJOR 1)
7 set(HG2G_VERSION_MINOR 42)
8 set(HG2G_VERSION_PATCH 0)
9 string(CONCAT HG2G_VERSION "${HG2G_VERSION_MAJOR}"
10 ".${HG2G_VERSION_MINOR}" ".${HG2G_VERSION_PATCH}")
11
12 # Process the subdirectories hg2g and app.
13 add_subdirectory(hg2g)
14 add_subdirectory(app)
app/CMakeLists.txt
1 # Add a program target called answer.
2 add_executable(answer answer.cpp)
3
4 # Link the answer program against the hg2g library.
5 target_link_libraries(answer hg2g)
6
7 # Install the answer program in the bin directory.
8 install(TARGETS answer DESTINATION bin)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2017
HG2G Example: CMakeLists Files (Continued 1)
hg2g/CMakeLists.txt
1 # Place the names of the header and source files into
2 # variables (for convenience).
3 set(hg2g_headers include/hg2g/answer.hpp
4 "${CMAKE_CURRENT_BINARY_DIR}/include/hg2g/config.hpp")
5 set(hg2g_sources answer.cpp question.cpp)
6
7 # Add a library target called hg2g.
8 add_library(hg2g ${hg2g_sources} ${hg2g_headers})
9
10 # Specify the include directories for library.
11 target_include_directories(hg2g PUBLIC
12 include
13 "${CMAKE_CURRENT_BINARY_DIR}/include")
14
15 # Create a header file containing the config information.
16 configure_file(
17 include/hg2g/config.hpp.in
18 "${CMAKE_CURRENT_BINARY_DIR}/include/hg2g/config.hpp")
19
20 # Install the library in the lib directory.
21 install(TARGETS hg2g DESTINATION lib)
22
23 # Install the header files in the include/hg2g directory.
24 install(FILES ${hg2g_headers} DESTINATION include/hg2g)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2018
External Project Example
hello, hg2g, and example 100 are subdirectories containing CMake projects
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
2 # Specify the project and do not enable any languages.
3 project(examples LANGUAGES CXX)
4 # Include the module for external project functionality.
5 include(ExternalProject)
6 # Create a list of the subdirectories containing
7 # CMake projects to be built.
8 list(APPEND dirs hello hg2g "example 100")
9 # Add each project as an external project.
10 foreach(dir IN LISTS dirs)
11 # Set target name to directory name with any
12 # spaces changed to underscores.
13 string(REPLACE " " "_" target "${dir}")
14 # Add external project.
15 externalproject_add("${target}"
16 SOURCE_DIR "${CMAKE_SOURCE_DIR}/${dir}"
17 BINARY_DIR "${CMAKE_BINARY_DIR}/${dir}"
18 CMAKE_ARGS
19 "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
20 INSTALL_COMMAND "")
21 endforeach()
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2019
LATEX Example
want to build LATEX document (i.e., produce PDF document from LATEX
source)
files in project:
CMakeLists.txt
main.tex
bib.bib
cmake_modules/UseLATEX.cmake
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2020
LATEX Example: Source Code
main.tex
1 \documentclass{article}
2 \usepackage{graphicx}
3 \author{John Doe}
4 \title{Why I Like C++}
5 \begin{document}
6 \maketitle
7 \section{Why I Like C++}
8 What can I say?
9 C++˜\cite{TCPL4} is a great language!\newline
10 \includegraphics[width=1in,height=1in,keepaspectratio]
11 {cpp.png}
12 \bibliographystyle{plain}
13 \bibliography{bib}
14 \end{document}
bib.bib
1 @book{
2 TCPL4,
3 author = "B. Stroustrup",
4 title = "The {C++} Programming Language",
5 edition = "4th",
6 publisher = "Addison Wesley",
7 year = 2013
8 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2021
LATEX Example: CMakeLists File
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
2
3 # Specify the project name and indicate that no languages
4 # should be enabled.
5 project(my_doc NONE)
6
7 # Add the cmake_modules directory to the module search path.
8 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
9 ${CMAKE_SOURCE_DIR}/cmake_modules)
10
11 # Include the UseLATEX module.
12 include(UseLATEX)
13
14 # Specify the properties of the LaTeX document such as its
15 # constituent source files (e.g., LaTeX, BibTeX, images,
16 # figures, etc.)
17 add_latex_document(main.tex IMAGES cpp.png BIBFILES bib.bib)
cmake_modules/UseLATEX.cmake
This file is taken from https://cmake.org/Wiki/images/8/80/
UseLATEX.cmake.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2022
Section 9.3.3
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2023
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2024
Talks
1 Bill Hoffman, Google Tech Talk — Building Science With CMake, October
8, 2015. Available online at https://youtu.be/TqjtN8NGtl4.
A very basic introduction to CMake.
2 Daniel Pfeifer, Effective CMake, C++ Now, May 19, 2017, Aspen, CO,
USA. Available online at https://youtu.be/bsXLMQ6WgIk.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2025
Part 10
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2026
Section 10.1
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2027
Version Control Systems
Version control (also known as revision control) is the management of
changes to programs, documents, and other collections of information.
In practice, multiple versions of the same software will often be in
existence at any given time.
For the purposes of locating and fixing a bug, it is critically important to be
able to access different versions of the software, since only certain
versions of the software may have the bug.
When concurrently developing multiple versions of some software, it is
necessary to be able to keep track of what information belongs to which
versions.
Having developers manually maintain version information themselves is
impractical, as this would be very error prone.
Therefore, a version control system (VCS) is used to manage changes in
a systematic manner.
Some examples of VCSes include: Source Code Control System (SCCS),
Revision Control System (RCS), Concurrent Versions System (CVS)
Subversion, Mercurial, and Git.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2028
Centralized Version Control
Server
Repository
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2029
Distributed Version Control
Server
Repository
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2030
Pros and Cons of Distributed Version Control
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2031
Section 10.2
Git
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2032
Git
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2033
Users of Git
Git has a very large user base and is employed heavily in industry
some organizations using Git include:
Apple (https://github.com/apple)
eBay (https://github.com/eBay)
Facebook (https://github.com/facebook)
Google (https://github.com/google)
Intel (https://github.com/intel)
Microsoft (https://github.com/Microsoft)
NVIDIA (https://github.com/NVIDIA)
Twitter (https://github.com/twitter)
Gnome (https://git.gnome.org)
Eclipse (https://git.eclipse.org)
KDE (https://github.com/KDE)
FreeDesktop (https://cgit.freedesktop.org)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2034
Repositories
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2035
Revision History and Directed Acyclic Graphs
C3 C5
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2036
Branching Workflows
single (master) branch:
master
M1 M2 M3 M4 M5 M6
master
M1 M2 develop
D1 D2 D3 topic
T1 T2
Stage Commit
Working Tree Index Repository
index (also known as staging area): place where changes that are
repository
staging: applies changes in working tree to index
committing: applies changes in index to repository
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2038
Local and Remote Picture
Fetch/Pull
Push
Local Repository Remote Repository
merging changes
pull: propagate changes from remote repository to local repository and
merge changes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2039
HEAD
C1 C2 C3
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2040
Remote-Tracking Branches
Consider a remote repository whose commit history is as shown below,
with a single branch master.
HEAD master
C1 C2 C3
[remote repository]
Cloning the above repository will produce a new local repository whose
commit history is as shown below, with a (local) branch master and a
remote-tracking branch origin/master.
HEAD master
C1 C2 C3
origin/master
[local repository]
A branch fetched from a remote repository is called a remote-tracking
branch.
A remote-tracking branch is a reference to a commit in the remote
repository and is used for operations like pushing and fetching/pulling.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2041
Commit History Example I
C1 C2
2 Cloning the remote repository yields a new local repository that is identical
to the remote repository but with a remote-tracking branch
origin/master added as follows:
HEAD master
C1 C2
origin/master
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2042
Commit History Example II
C1 C2 −→ C1 C2 A1
origin/master origin/master
C1 C2 A1 −→ C1 C2 A1 A2
origin/master origin/master
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2043
Commit History Example III
−→
C1 C2 C1 C2 B1 B2
6 Fetching (to the local repository) from the remote repository transforms
the local repository as follows:
HEAD master
HEAD master
C1 C2 A1 A2
C1 C2 A1 A2 −→
B1 B2
origin/master
origin/master
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2044
Commit History Example IV
7 Merging the origin/master branch into the master branch (in the local
repository) transforms the local repository as follows:
HEAD master
C1 C2 A1 A2 A3
C1 C2 A1 A2
−→ B1 B2 master
B1 B2
HEAD
origin/master
origin/master
8 Pushing (from the local repository) to the remote repository transforms the
remote and local repositories, respectively, as follows:
HEAD master C1 C2 A1 A2 A3
−→
C1 C2 B1 B2 B1 B2 master HEAD
C1 C2 A1 A2 A3 origin/master
B1 B2 master −→ C1 C2 A1 A2 A3
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2045
Commit History Example: Commands to Setup Remote
1 TOP_DIR=‘pwd‘
2
3 cd $TOP_DIR
4 mkdir remote
5 cd remote
6 git init
7 printf "apple\n" >> fruits.txt
8 git add fruits.txt
9 git commit -m "Added file fruits.txt" # Commit C1
10 printf "banana\n" >> fruits.txt
11 git add fruits.txt
12 git commit -m "Added banana to fruits.txt" # Commit C2
13 git init --bare .git
14 mv .git $TOP_DIR/remote.git
15 cd $TOP_DIR
16 rm -rf remote
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2046
Commit History Example: Remaining Commands
1 cd $TOP_DIR
2 git clone remote.git local-1
3
4 cd $TOP_DIR/local-1
5 printf "grape\n" >> fruits.txt
6 git add fruits.txt
7 git commit -m "Added grape to fruits.txt" # Commit A1
8 printf "orange\n" >> fruits.txt
9 git add fruits.txt
10 git commit -m "Added orange to fruits.txt" # Commit A2
11
12 cd $TOP_DIR
13 git clone remote.git local-2
14 cd local-2
15 printf "red\n" >> colors.txt
16 git add colors.txt
17 git commit -m "Added file colors.txt" # Commit B1
18 printf "green\n" >> colors.txt
19 git add colors.txt
20 git commit -m "Added green to colors.txt" # Commit B2
21 git push
22
23 cd $TOP_DIR/local-1
24 git push # ERROR: local repository not up to date
25 git fetch
26 git merge -m "Merged changes." # Commit A3
27 git push
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2047
Git Configuration
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2048
Git on One Slide
Configure user information and clone the repository:
git config --global user.name "John Doe"
git config --global user.email [email protected]
git clone $repository $directory
Edit the working tree and stage changes as appropriate for the local
repository:
git add $path_to_add
git mv $source_path $destination_path
git rm $file_to_remove
git rm -r $directory_to_remove
Check what changes are staged and then commit these changes to the
local repository:
git status
git commit
Push changes from the local repository to the remote repository:
git push
As needed, retrieve changes from the remote repository and merge them
locally (e.g., if a push failed due to being out of date):
git pull
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2049
Section 10.2.1
Basic Commands
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2050
Determining the Version of Git
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2051
Obtaining Help on the git Command
To obtain general help for the git command, use a command of the form:
git help [options]
To obtain detailed information for the git command or guide $item, use a
command of the form:
git help [options] $item
Some commonly-used options include:
Option Description
-a list all commands for which help available
-g list all available help guides
-w display in HTML format using a web browser
-m display in man (i.e., manual page) format
To obtain detailed help on the commit command with the information
displayed in HTML format in a web browser, type:
git help -w commit
To list all of the help guides available, type:
git help -g
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2052
Configuring Git
To set the variable $name to the value $value, use a command of the
form:
git config [options] $name $value
To unset the variable $name, use a command of the form:
git config [options] --unset $name
To list all of the current variables settings, use a command of the form:
git config [options] -l
Some commonly-used options include:
Option Description
--system consider only the system-wide settings
--global consider only the global (i.e., per-user) settings
--local consider only the local (i.e., per-repository) settings
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2053
Some Commonly-Used Git Variables
Variable Description
core.askPass program for entering user name and password
credentials
core.editor program for editing
core.pager program for paging output
credential.helper external program to be called when a user
name or password credential is needed
user.name user’s full name
user.email user’s email address
web.browser program for browsing web
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2054
Configuring User Information
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2055
Configuring User-Credential-Related Information
To enable the global caching of user credentials for 1 hour (i.e.,
3600 seconds), type:
git config --global \
credential.helper ’cache --timeout=3600’
To disable all caching of user credentials (i.e., at the system, global, and
repository levels) and purge any cached values, type:
git config --unset credential.helper
git config --global --unset credential.helper
git config --system --unset credential.helper
git credential-cache exit
To ensure that prompting for user credentials employs standard
input/output (as opposed to, say, a pop-up window), type:
git config --unset core.askPass
git config --global --unset core.askPass
git config --system --unset core.askPass
unset GIT_ASKPASS
unset SSH_ASKPASS
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2056
Creating an Empty Repository
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2057
Cloning a Repository
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2058
Adding Files/Directories to the Index
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2059
Removing Files/Directories from the Index
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2060
Renaming Files
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2061
Removing Files
To remove the files/directories $path... from the working tree and the
index, use a command of the form:
git rm [options] $path...
Some commonly-used options include:
Option Description
-f override the up-to-date check
-r if the given path is a directory, recursively remove files be-
low it
To remove the directory src and all files and directories beneath it from
the working tree and index, type:
git rm -r src
To remove the files README and LICENSE from the working tree and index,
type:
git rm README LICENSE
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2062
Committing Changes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2063
Checking the Status of the Working Tree
To display the status of the working tree, use a command of the form:
git [options] status
Some commonly-used options include:
Option Description
--long give the output in long format (default)
--short give the output in short format
The information displayed by this command includes:
paths (i.e., files and directories) that have differences between the index
and the current HEAD commit, (i.e., what would be committed by running
git commit)
paths that have differences between the working tree and index as well as
paths that are not tracked by Git (i.e., what could by committed by running
git add before git commit)
To display the status of the working tree in long form, type:
git status
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2064
Showing Commit Logs
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2065
Showing Commit Logs (Continued)
To show the commit history for the file README since 2016-01-01, type:
git log --since 2016-01-01 README
To show the commit history for all files between 2014-01-01 and
2014-12-31, type:
git log --since 2014-01-01 --until 2014-12-31
To show the commit history for all files in all branches with a text-based
graph, type:
git log --all --graph
To show the commit history for all commits made since v1.0 until and
including v2.0 (assuming that v1.0 and v2.0 exist), type:
git log v1.0..v2.0
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2066
Showing Changes
To show changes between the working tree and the index (i.e., what could
be staged but has not yet been) for files/directories $path... (which
defaults to all files/directories), use a command of the form:
git diff [options] [$path...]
To show changes between the index and the named commit $commit
(which defaults to HEAD) for the files/directories $path... (which defaults
to all files/directories), use a command of the form:
git diff [options] --cached [$commit] -- \
[$path...]
To show changes between the working tree and the named commit
$commit (which defaults to HEAD) for the files/directories $path...
(which defaults to all files/directories), use a command of the form:
git diff [options] [$commit] -- [$path...]
To show changes between two arbitrary commits $commit1 and
$commit2 for the files/directories $path..., use a command of the form:
git diff [options] $commit1 $commit2 -- \
[$path...]
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2067
Showing Changes (Continued)
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2068
Finding Lines Matching a Pattern
To find all lines of text in the files $path... (which defaults to all files) in
the working tree that satisfy the condition specified by the p_options,
use a command of the form:
git grep [options] [p_options] -- [$path...]
Some commonly-used options include:
Option Description
-l print only names of files with matches
-i ignore case
--max-depth $depth descend at most $depth levels of directories
-v select non-matching lines
-F patterns are fixed strings
-E patterns are extended POSIX regular expressions
-e $pattern specify pattern $pattern
--and logical and
--or logical or
--not logical not
( for grouping logical operations
) for grouping logical operations
--cache search in the index instead of the working tree
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2069
Finding Lines Matching a Pattern (Continued)
To search for the text “hello” in a case insensitive manner in all of the
files in the working tree, type:
git grep -i -e hello
To print only the names of the files that match the pattern specified in the
preceding example, type:
git grep -i -e hello -l
To find all of the files in the working tree with suffixes “.cpp” or “.hpp” that
have lines containing either “#include <vector>” or
“#include <list>”, type:
git grep -e ’#include <vector>’ --or \
-e ’#include <list>’ -- ’*.cpp’ ’*.hpp’
To perform the same search as the preceding example but in the index
rather than the working tree, type:
git grep --cache -e ’#include <vector>’ --or \
-e ’#include <list>’ -- ’*.cpp’ ’*.hpp’
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2070
Removing Untracked Files and Directories
To remove all untracked files in the working tree, use a command of the
form:
git clean [options]
Some commonly-used options include:
Option Description
-d remove untracked directories in addition to untracked files
-f force removal of files
-i enable interactive mode
-n show what would be done without actually doing anything
-x do not use standard ignore rules (such as those specified in
.gitignore files)
To remove all untracked files and directories in the working tree excluding
those ignored by Git, type:
git clean -d -f
To remove all untracked files and directories in the working tree including
those ignored by Git, type:
git clean -d -f -x
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2071
.gitignore Files
A .gitignore file specifies which files and directories are intentionally
untracked and should be ignored by Git.
The purpose of a .gitignore file is to ensure that certain files not
tracked by Git remain untracked.
The .gitignore file lists patterns specifying files that should be ignored
by Git.
A “!” prefix negates a pattern.
A leading slash matches the directory containing the .gitignore file. For
example, /hello.cpp matches hello.cpp but not
some/subdirectory/hello.cpp.
The patterns in a .gitignore file apply to the directory containing the file
as well as all directories below the file in the working tree.
The patterns in a .gitignore file at a higher level in the tree are
overridden by patterns in a .gitignore file at a lower level.
A .gitignore file in the root directory of the working tree can be used to
establish ignore defaults for the whole tree.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2072
.gitignore File Example
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2073
.gitattributes Files
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2074
Tracking Empty Directories
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2075
Section 10.2.2
Remote-Related Commands
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2076
Listing, Adding, and Removing Remotes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2078
Pushing Changes to Another Repository
To push changes to the branch $branch (which normally defaults to the
current branch) of the remote $remote (which normally defaults to
origin), use a command of the form:
git push [options] [$remote [$branch]]
When pushing a new local branch to a remote, the -u option should be
specified.
To delete the branch $branch on the remote $remote only, use a
command of the form:
git push --delete origin $branch
The preceding command is useful if one wants to delete a branch that
exists on the remote but not in the local repository.
To delete the tag $tag on the remote $remote only, use a command of
the form:
git push --delete origin $tag
To push to the default remote and branch, type:
git push
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2079
Pulling Changes from Another Repository
To pull changes from the branch $branch of the remote $remote, use a
command of the form:
git pull [$remote [$branch]]
To pull from the default remote and branch, type:
git pull
A pull is approximately a fetch followed by merge.
A pull automatically merges commits without letting them be reviewed first.
For this reason, some people suggest that it is better to use fetch and
merge separately instead of performing a pull.
Also, the use of pull operations can, in some cases, result in unnecessary
merge commits.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2080
Merging Changes
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2081
Section 10.2.3
Branch-Related Commands
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2082
Listing, Creating, and Deleting Branches
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2083
Checking Out a Branch
To checkout (i.e., switch to) the branch $branch, use a command of the
form:
git checkout $branch
Checking out a branch changes the files/directories of the working tree to
match that branch.
If you have local modifications to one or more files that are different
between the current branch and the branch to which you are switching,
the command refuses to switch branches in order to preserve your
modifications in context.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2084
Section 10.2.4
Tag-Related Commands
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2085
Listing, Creating, and Deleting Tags
To list all tags, type:
git tag
To tag a commit $commit (which defaults to HEAD) with the name $name,
use a command of the form:
git tag [options] $name [$commit]
To delete the (local) tags with names $name..., use a command of the
form:
git tag -d $name...
Some commonly-used options include:
Option Description
-a make an unsigned annotated tag
To create an annotated tag version-1.0 for the most recent commit on
the master branch, type:
git tag -a version-1.0 master
To delete the tag version-1.0, type:
git tag -d version-1.0
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2086
Pushing Tags
To push a tag $tag to the remote $remote, use a command of the form:
git push $remote $tag
To push the tag v1.0 to the remote origin, type:
git push origin v1.0
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2087
Section 10.2.5
Miscellany
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2088
Duplicating a Repository
can create exact duplicate of entire Git repository (including all tags and
local branches) by using bare-clone and mirror-push operations
to copy repository $source_repo to (already existing) remote repository
$destination_repo (overwriting contents of repository), use command
sequence:
# Create a bare clone of the repository.
git clone --bare $source_repo $bare_dir
# Mirror push to the destination repository.
git -C $bare_dir push --mirror $destination_repo
# Remove the temporary local bare repository.
rm -rf $bare_dir
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2089
Avoiding Repeated Passphrase Entry for SSH Authentication
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2090
Additional Remarks
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2091
Git-Related Software
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2092
Section 10.2.6
References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2093
References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2094
References II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2095
Talks
1 Linus Torvalds, Google Tech Talk: Linus Torvalds on git — Git: Source
code control the way it was meant to be!, May 2007. Available online at
https://youtu.be/4XpnKHJAok8.
Linus Torvalds shares his thoughts on Git, the source control management
system he created.
2 Matthew McCullough, The Basics of Git and GitHub, July 2013. Available
online at https://youtu.be/U8GBXvdmHT4.
This is an excellent introduction to using Git.
3 Scott Chacon, Introduction to Git with Scott Chacon of GitHub, June 2011.
Available online at https://youtu.be/ZDR433b0HJY.
This is another popular introduction to using Git.
4 Matthew McCullough, Advanced Git: Graphs, Hashes, and Compression,
Oh My!, Sept. 2012. Available online at https://youtu.be/
ig5E8CcdM9g.
This is a very good more advanced talk on Git.
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2096
Part 11
Miscellany
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2097
What Is Wrong With This Code?
foo.hpp
1 #ifndef foo_hpp
2 #define foo_hpp
3 namespace foo {
4 bool is_odd(int x) {return (x % 2) != 0;}
5 bool is_even(int x) {return (x % 2) == 0;}
6 }
7 #endif
main.cpp
1 #include <iostream>
2 #include "foo.hpp"
3 int main() {
4 std::cout << foo::is_odd(42) << ’ ’ <<
5 foo::is_even(42) << ’\n’;
6 }
other.cpp
1 #include "foo.hpp"
2 // ...
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2098
Solution: Functions Should Be Inline
foo.hpp
1 #ifndef foo_hpp
2 #define foo_hpp
3 namespace foo {
4 inline bool is_odd(int x) {return (x % 2) != 0;}
5 inline bool is_even(int x) {return (x % 2) == 0;}
6 }
7 #endif
main.cpp
1 #include <iostream>
2 #include "foo.hpp"
3 int main() {
4 std::cout << foo::is_odd(42) << ’ ’ <<
5 foo::is_even(42) << ’\n’;
6 }
other.cpp
1 #include "foo.hpp"
2 // ...
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2099
What Is Wrong With This Code?
foo.hpp
1 #ifndef foo_hpp
2 #define foo_hpp
3 namespace foo {
4 inline bool is_odd(int x);
5 inline bool is_even(int x);
6 }
7 #endif
foo.cpp
1 #include "foo.hpp"
2 namespace foo {
3 bool is_odd(int x) {return (x % 2) != 0;}
4 bool is_even(int x) {return (x % 2) == 0;}
5 }
app.cpp
1 #include <iostream>
2 #include "foo.hpp"
3 int main() {
4 std::cout << foo::is_odd(42) << ’ ’ <<
5 foo::is_even(42) << ’\n’;
6 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2100
Solution: Place Inline Function Definitions in Header File
foo.hpp
1 #ifndef foo_hpp
2 #define foo_hpp
3 namespace foo {
4 inline bool is_odd(int x) {return (x % 2) != 0;}
5 inline bool is_even(int x) {return (x % 2) == 0;}
6 }
7 #endif
app.cpp
1 #include <iostream>
2 #include "foo.hpp"
3 int main() {
4 std::cout << foo::is_odd(42) << ’ ’ <<
5 foo::is_even(42) << ’\n’;
6 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2101
What Is Wrong With This Code?
foo.hpp
1 #ifndef foo_hpp
2 #define foo_hpp
3 namespace foo {
4 template <typename T> T abs(const T& x);
5 }
6 #endif
foo.cpp
1 #include "foo.hpp"
2 namespace foo {
3 template <typename T> T abs(const T& x)
4 {return (x < 0) ? (-x) : x;}
5 }
app.cpp
1 #include <iostream>
2 #include "foo.hpp"
3 int main() {
4 std::cout << foo::abs(-42) << ’ ’ <<
5 foo::abs(-3.14) << ’\n’;
6 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2102
First Solution: Explicit Template Instantiation
foo.hpp
1 #ifndef foo_hpp
2 #define foo_hpp
3 namespace foo {
4 template <typename T> T abs(const T& x);
5 }
6 #endif
foo.cpp
1 #include "foo.hpp"
2 namespace foo {
3 template <typename T> T abs(const T& x)
4 {return (x < 0) ? (-x) : x;}
5 template int abs<int>(const int&);
6 template double abs<double>(const double&);
7 }
app.cpp
1 #include <iostream>
2 #include "foo.hpp"
3 int main() {
4 std::cout << foo::abs(-42) << ’ ’ <<
5 foo::abs(-3.14) << ’\n’;
6 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2103
Second Solution: Define Function Template in Header File
foo.hpp
1 #ifndef foo_hpp
2 #define foo_hpp
3 namespace foo {
4 template <typename T> T abs(const T& x)
5 {return (x < 0) ? (-x) : x;}
6 }
7 #endif
app.cpp
1 #include <iostream>
2 #include "foo.hpp"
3 int main() {
4 std::cout << foo::abs(-42) << ’ ’ <<
5 foo::abs(-3.14) << ’\n’;
6 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2104
Remarks on Headers Files and Function Declarations
foo.hpp
1 #ifndef foo_hpp
2 #define foo_hpp
3 #include <cmath>
4 namespace foo {
5 double log(double x, double b);
6 }
7 #endif
foo.cpp
1 #include <cmath>
2 #include "foo.hpp"
3 namespace foo {
4 double log(double x, double b = 10.0)
5 {return std::log(x) / std::log(b);}
6 }
app.cpp
1 #include <iostream>
2 #include "foo.hpp"
3 int main() {
4 std::cout << foo::log(16.0, 2.0) << ’ ’ <<
5 foo::log(10.0) << ’\n’;
6 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2106
Solution: Place Default Arguments in Header File
foo.hpp
1 #ifndef foo_hpp
2 #define foo_hpp
3 #include <cmath>
4 namespace foo {
5 double log(double x, double b = 10.0);
6 }
7 #endif
foo.cpp
1 #include <cmath>
2 #include "foo.hpp"
3 namespace foo {
4 double log(double x, double b)
5 {return std::log(x) / std::log(b);}
6 }
app.cpp
1 #include <iostream>
2 #include "foo.hpp"
3 int main() {
4 std::cout << foo::log(16.0, 2.0) << ’ ’ <<
5 foo::log(10.0) << ’\n’;
6 }
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2107
Part 12
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2108
Limits of Knowledge
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2109
C++ References
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2110
C++ References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2111
C++ References II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2112
C++ References III
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2113
C++ References IV
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2114
C++ References V
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2115
C++ References VI
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2116
Other C++ References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2117
Other C++ References II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2118
Other C++ References III
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2119
Yet More C++ References I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2120
Yet More C++ References II
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2121
Miscellaneous Talks I
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2122
C++ Programming Competitions
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2123
The Last Word
Write code!
Write lots and lots and lots of code!
The only way to truly learn a programming language well is to use it
heavily (i.e., write lots of code using the language).
Copyright
c 2015–2018 Michael D. Adams C++ Version: 2018-02-15 2124