Lecture Slides For Programming in c++-2019-02-04
Lecture Slides For Programming in c++-2019-02-04
Michael D. Adams
To obtain the most recent version of these lecture slides (with functional hyperlinks) or for additional
information and resources related to these slides (including errata and lecture videos), please visit:
http://www.ece.uvic.ca/˜mdadams/cppbook
If you like these lecture slides, please consider posting a review of them at:
https://play.google.com/store/search?q=ISBN:9781550586411 or
http://books.google.com/books?vid=ISBN9781550586411
Copyright ⃝
c 2015, 2016, 2017, 2018, 2019 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/
UNIX and X Window System are registered trademarks of The Open Group. Windows is a registered trademark of Microsoft Corporation.
Fedora is a registered trademark of Red Hat, Inc. Ubuntu is a registered trademark of Canonical Ltd. MATLAB is a registered trademark of The
MathWorks, Inc. OpenGL is a registered trademark of Hewlett Packard Enterprise. The YouTube logo is a registered trademark of Google, Inc.
The GitHub logo is a registered trademark of GitHub, Inc. The Twitter logo is a registered trademark of Twitter, Inc.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 iii
License II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 iv
License III
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 v
License IV
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 vi
License V
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 vii
License VI
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 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–2019 Michael D. Adams C++ Version: 2019-02-04 ix
License VIII
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 x
Other Textbooks and Lecture Slides by the Author I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 xi
Other Textbooks and Lecture Slides by the Author II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 xii
Other Textbooks and Lecture Slides by the Author III
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 xiii
Part 0
Preface
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 xiv
About These Lecture Slides
■ Many aspects of the C++ language are covered from introductory to more
advanced.
■ Some aspects of the C++ standard library are also introduced.
■ In addition, various general programming-related topics are considered.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 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–2019 Michael D. Adams C++ Version: 2019-02-04 xvi
Disclaimer
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 xvii
Typesetting Conventions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 xviii
Companion Web Site
■ The author of the lecture slides maintains a companion web site for the
slides.
■ The most recent version of the slides can be downloaded from this site.
■ Additional information related to the slides is also available from this site,
including:
2 errata for the slides; and
2 information on the companion web site, companion Git repository, and
companion YouTube channel for the slides.
■ The URL of this web site is:
2 http://www.ece.uvic.ca/ mdadams/cppbook
˜
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 xix
Companion Git Repository
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 xx
Companion YouTube Channel
■ Video lectures for some of the material covered by these lecture slides
can be found on the author’s YouTube channel.
■ The URL of the author’s YouTube channel is:
2 https://www.youtube.com/user/iamcanadian1867
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 xxi
Software Development Environment (SDE)
■ For more information about the SDE, refer to the main repository page on
GitHub.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 xxii
Part 1
Software
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1
Why Is Software Important?
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2
Why Software-Based Solutions?
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 3
Software-Related Jobs
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 4
C
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 5
C++
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 6
Java
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 7
Fortran
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 8
C#
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 9
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–2019 Michael D. Adams C++ Version: 2019-02-04 10
MATLAB
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 11
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++
■ popular language
■ consistently ranks amongst top languages in TIOBE Software
Programming Community Index
(https://www.tiobe.com/tiobe-index/)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 12
Part 2
C++
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 13
Section 2.1
History of C++
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 14
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–2019 Michael D. Adams C++ Version: 2019-02-04 15
Objectives
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 17
Timeline for C with Classes (1979–1983) II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 18
Timeline for C with Classes (1979–1983) III
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 19
Timeline for C84 to C++98 (1982–1998) I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 22
Timeline for C84 to C++98 (1982–1998) IV
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 23
Timeline for C84 to C++98 (1982–1998) V
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 24
Timeline for C84 to C++98 (1982–1998) VI
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 25
Timeline for C84 to C++98 (1982–1998) VII
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 26
Timeline for C84 to C++98 (1982–1998) VIII
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 27
Timeline for C84 to C++98 (1982–1998) IX
Jun 1991 second edition of C++PL published
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 28
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–2019 Michael D. Adams C++ Version: 2019-02-04 30
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–2019 Michael D. Adams C++ Version: 2019-02-04 31
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–2019 Michael D. Adams C++ Version: 2019-02-04 32
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–2019 Michael D. Adams C++ Version: 2019-02-04 34
Additional Comments
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 35
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–2019 Michael D. Adams C++ Version: 2019-02-04 36
Success of C++
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 39
Evolution of C++
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 42
Standards Documents III
■ 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 TS 22277:2017 — technical specification — C++ extensions for
coroutines, Nov. 2017.
■ ISO/IEC 14882:2017 — information technology — programming
languages — C++, Dec. 2017.
■ ISO/IEC TS 19216:2018 — programming languages — C++ extensions
for networking, Apr. 2018.
■ ISO/IEC TS 21544:2018 — programming languages — extensions to C++
for modules, May 2018.
■ ISO JTC1/SC22/WG21 web site.
http://www.open-std.org/jtc1/sc22/wg21/.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 43
Section 2.2
Getting Started
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 44
hello Program: hello.cpp
1 #include <iostream>
2
3 int main()
4 {
5 std::cout << "Hello, world!\n";
6 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 45
Software Build Process
Source Code Compile Link
Object File Executable
File
(.o) Program
(.cpp, .hpp)
.. .. .. ..
. . . .
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 46
GNU Compiler Collection (GCC) C++ Compiler
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 47
Common g++ Command-Line Options
■ -c
2 compile only (i.e., do not link)
■ -o file
2 use file file for output
■ -g
2 include debugging information
■ -On
2 set optimization level to n (0 almost none; 3 full)
■ -std=c++17
2 conform to C++17 standard
■ -Idir
2 specify additional directory dir to search for include files
■ -Ldir
2 specify additional directory dir to search for libraries
■ -llib
2 link with library lib
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 48
Common g++ Command-Line Options (Continued 1)
■ -pthread
2 enable concurrency support (via pthreads library)
■ -pedantic-errors
2 strictly enforce compliance with standard
■ -Wall
2 enable most warning messages
■ -Wextra
2 enable some extra warning messages not enabled by -Wall
■ -Wpedantic
2 warn about deviations from strict standard compliance
■ -Werror
2 treat all warnings as errors
■ -fno-elide-constructors
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 49
Common g++ Command-Line Options (Continued 2)
■ -fconstexpr-loop-limit=n
2 set maximum number of iterations for loop in constexpr functions to n
■ -fconstexpr-depth=n
2 set maximum nested evaluation depth for constexpr functions to n
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 50
Clang C++ Compiler
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 51
Common clang++ Command-Line Options
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 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–2019 Michael D. Adams C++ Version: 2019-02-04 53
Section 2.3
C++ Basics
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 54
The C++ Programming Language
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 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 [C++17 5.10/1]
⁓⁓⁓⁓⁓⁓⁓
■ identifiers that begin with underscore (in many cases) or contain double
underscores are reserved for use by C++ implementation and should be
avoided ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 5.10/3]
2 sqrt_2
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–2019 Michael D. Adams C++ Version: 2019-02-04 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–2019 Michael D. Adams C++ Version: 2019-02-04 58
Section 2.3.1
Preprocessor
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 59
The Preprocessor
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 60
Source-File Inclusion
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 61
Defining Macros
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 62
Conditional Compilation
4 #endif directive
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 63
Preprocessor Predicate __has_include
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 64
Section 2.3.2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 65
Fundamental Types
■ boolean type: bool
■ character types:
2 char (may be signed or unsigned)
2 signed char
2 unsigned char
2 char16_t
2 char32_t
2 wchar_t
2 signed char
2 signed short int
2 signed int
2 signed long int
2 signed long long int
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 66
Fundamental Types (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 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–2019 Michael D. Adams C++ Version: 2019-02-04 69
Character Literals (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 70
String Literals
■ (non-raw) string literal consists of optional prefix followed by zero or more
characters enclosed in double quotes
■ string literal has character array type
■ type of string literal determined by prefix (or lack thereof) as follows:
Prefix Literal Type
None narrow const char[]
u8 UTF-8 const char[]
u UTF-16 const char16_t[]
U UTF-32 const char32_t[]
L wide const wchar_t[]
■ examples of string literals:
"Hello, World!\n"
"123"
"ABCDEFG"
■ adjacent string literals are concatenated (e.g., "Hel" "lo" equivalent to
"Hello")
■ string literals implicitly terminated by null character (i.e., ’\0’)
■ so, for example, "Hi" means ’H’ followed by ’i’ followed by ’\0’
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 71
Raw String Literals
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 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:
2 u or U
2 l or L
2 both u or U and l or L
2 ll or LL
2 both u or U and ll or LL
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 74
Floating-Point Literals
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 75
Hexadecimal Floating-Point Literals
■ hexadecimal floating-point literal has general form:
1 prefix 0x or 0X
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–2019 Michael D. Adams C++ Version: 2019-02-04 76
Boolean and Pointer Literals
■ boolean literals:
true
false
■ pointer literal:
nullptr
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 77
Declarations and Definitions
■ declaration introduces identifier for type, object (i.e., variable), or function
(without necessarily providing full information about identifier)
2 in case of object, specifies type (of object)
2 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
2 in case of type, provides full details about type
2 in case of object, causes storage to be allocated for object and object to be
created
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 78
Examples of Declarations and Definitions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 79
Variable Declarations and Definitions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 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–2019 Michael D. Adams C++ Version: 2019-02-04 81
Array Example
■ code:
int a[4] = {1, 2, 3, 4};
■ assumptions (for some completely fictitious C++ language
implementation):
2 sizeof(int) is 4
2 array a starts at address 1000
■ memory layout:
Address Name
1000 1 a[0]
1004 2 a[1]
1008 3 a[2]
1012 4 a[3]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 82
Pointers
■ code:
int i = 42;
int* p = &i;
assert(*p == 42);
■ assumptions (for some completely fictitious C++ language
implementation):
2 sizeof(int) is 4
2 sizeof(int*) is 4
2 &i is ((int*)1000)
2 &p is ((int*)1004)
■ memory layout:
Address Name
1000 42 i
1004 1000 p
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 84
References
■ code:
int i = 42;
int& j = i;
assert(j == 42);
■ assumptions (for some completely fictitious C++ language
implementation):
2 sizeof(int) is 4
2 &i is ((int*)1000)
■ memory layout:
Address Name
1000 42 i, j
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 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:
2 must be able to handle case of referring to nothing
2 must be able to change entity being referenced
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 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–2019 Michael D. Adams C++ Version: 2019-02-04 88
Scoped Enumerations
■ scoped enumeration similar to unscoped enumeration, except:
2 all enumerators are placed in scope of enumeration itself
2 integral type used to hold enumerator values can be explicitly specified
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 89
Type Aliases with typedef Keyword
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 90
Type Aliases with using Statement
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 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–2019 Michael D. Adams C++ Version: 2019-02-04 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–2019 Michael D. Adams C++ Version: 2019-02-04 93
The const Qualifier and Non-Pointer/Non-Reference Types
■ with types that are not pointer or reference types, const can only be
applied to object itself (i.e., top level)
■ that is, object itself may be const or non-const
■ example:
int i = 0; // object i is modifiable
i = 42; // OK: i can be modified
const int ci = 0; // object ci is not modifiable
ci = 42; // ERROR: ci cannot be modified
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 94
Example: const Qualifier and Non-Pointer/Non-Reference
Types
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 95
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–2019 Michael D. Adams C++ Version: 2019-02-04 96
Example: const Qualifier and Pointer Types
1 // with pointer types, const can be applied to each of:
2 // pointer and pointee
3 // pointer itself may be const or non-const (top-level)
4 // pointee may be const or non-const
5
6 int i = 0;
7 int j = 0;
8
9 int* pi = &i; // non-const pointer to a non-const int
10 pi = &j; // OK: can modify non-const pointer
11 *pi = 42; // OK: can modify non-const pointee
12
13 const int* pci = &i; // non-const pointer to a const int
14 // equivalently: int const* pci = &i;
15 pci = &j; // OK: can modify non-const pointer
16 *pci = 42; // ERROR: cannot modify const pointee
17
18 int* const cpi = &i; // const pointer to a non-const int
19 cpi = &j; // ERROR: cannot modify const pointer
20 *cpi = 42; // OK: can modify non-const pointee
21
22 const int* const cpci = &i; // const pointer to a const int
23 // equivalently: int const* const cpci = &i;
24 cpci = &j; // ERROR: cannot modify const pointer
25 *cpci = 42; // ERROR: cannot modify const pointee
26
27 pci = pi; // OK: adds const to pointee
28 pi = pci; // ERROR: discards const from pointee
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 97
The const Qualifier and Reference Types
■ reference is name that refers to object (i.e., referee)
■ in principle, const qualifier can be applied to reference itself (i.e.,
top-level qualifier) or referee
■ since reference cannot be rebound, reference itself is effectively always
constant
■ for this reason, does not make sense to explicitly apply const as
top-level qualifier for reference type and language disallows this
■ const qualifier can only be applied to referee
■ example:
int j = 0;
int k = 42;
int& i = j;
// i is reference; j is referee
// referee is modifiable
const int& ci = j;
// ci is reference; j is referee
// referee is not modifiable
const int& ci = k; // ERROR: cannot redefine/rebind
int& const r = j;
// ERROR: reference itself cannot be specified as const
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 98
Example: const Qualifier and Reference Types
1 // with reference types, const can only be applied to referee
2 // reference itself cannot be rebound (i.e., is constant)
3 // referee may be const or non-const
4
5 int i = 0; const int ci = 0;
6 int i1 = 0; const int ci1 = 0;
7
8 // reference to non-const int
9 int& ri = i;
10 ri = ci; // OK: can modify non-const referee
11 int& ri = i1; // ERROR: cannot redefine/rebind reference
12
13 // reference to const int
14 const int& rci = ci;
15 rci = i; // ERROR: cannot modify const referee
16 const int& rci = ci1;
17 // ERROR: cannot redefine/rebind reference
18
19 // ERROR: reference itself cannot be const qualified
20 int& const cri = i; // ERROR: invalid const qualifier
21
22 // ERROR: reference itself cannot be const qualified
23 const int& const crci = ci; // ERROR: invalid const qualifier
24 // also: int const& const crci = ci; // ERROR
25
26 const int& r1 = ci; // OK: adds const to referee
27 int& r2 = ci; // ERROR: discards const from referee
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 99
The const Qualifier and Pointer-to-Pointer Types
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 100
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:
2 corresponds to register of memory-mapped device
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 101
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–2019 Michael D. Adams C++ Version: 2019-02-04 102
Inline Variables
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 103
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–2019 Michael D. Adams C++ Version: 2019-02-04 104
Section 2.3.3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 105
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–2019 Michael D. Adams C++ Version: 2019-02-04 106
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–2019 Michael D. Adams C++ Version: 2019-02-04 107
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–2019 Michael D. Adams C++ Version: 2019-02-04 108
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–2019 Michael D. Adams C++ Version: 2019-02-04 109
Operators (Continued 4)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 110
Operator Precedence
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 111
Operator Precedence (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 112
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–2019 Michael D. Adams C++ Version: 2019-02-04 113
Operator Precedence (Continued 3)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 114
Operator Precedence (Continued 4)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 115
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–2019 Michael D. Adams C++ Version: 2019-02-04 116
Expressions
■ An expression has a type and, if the type is not void, a value. ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 6.9.1/9]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 117
Operator Precedence/Associativity Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 118
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 static_assert(5 % 3 == 2);
2 static_assert(5 % (-3) == 2);
3 static_assert((-5) % 3 == -2);
4 static_assert((-5) % (-3) == -2);
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 119
Short-Circuit Evaluation
2 groups left-to-right
2 result true if both operands are true, and false otherwise
2 second operand is not evaluated if first operand is false (in case of built-in
logical-and operator)
■ logical-or operator (i.e., ||): [C++17 8.15]
⁓⁓⁓⁓⁓⁓
2 groups left-to-right
2 result is true if either operand is true, and false otherwise
2 second operand is not evaluated if first operand is true (in case of built-in
logical-or operator)
■ example:
int x = 0;
bool b = (x == 0 || ++x == 1);
// b equals true; x equals 0
b = (x != 0 && ++x == 1);
// b equals false; x equals 0
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 120
Short-Circuit Evaluation Example: a || b || c
a
bool _result;
if (a) F
goto _true;
if (b) T b
goto _true;
if (c)
goto _true; T F
_result = false;
goto done; c
_true: T F
_result = true;
done:
True False
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 121
Short-Circuit Evaluation Example: a && b && c
a
bool _result;
if (!a) T
goto _false;
if (!b) b F
goto _false;
if (!c)
goto _false; T F
_result = true;
goto done; c
_false: F
_result = false;
done: T
True False
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 122
Short-Circuit Evaluation Example: (a || b) && c
a
bool _result;
if (a)
goto _second; F
if (!b)
goto _false; T b
_second:
if (!c) T F
goto _false;
_result = true; c
goto done;
_false: F
_result = false; T
done:
True False
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 123
The static_assert Statement
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 124
The sizeof Operator
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 125
The constexpr Qualifier for Variables
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 126
Section 2.3.4
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 127
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–2019 Michael D. Adams C++ Version: 2019-02-04 128
The if Statement (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 129
The if Statement (Continued 2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 132
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–2019 Michael D. Adams C++ Version: 2019-02-04 133
The switch Statement (Continued)
■ switch statement can also include initializer:
switch (initializer; expression)
statement
■ above construct equivalent to:
{
initializer;
switch (expression)
statement
}
■ remember that, in absence of break statement, execution in switch
statement falls through from one case to next; if fall through not
considered, bugs will result, such as in following code:
1 unsigned int x = 0;
2 switch (x & 1) {
3 case 0:
4 std::cout << "x is even\n";
5 // BUG: missing break statement
6 case 1:
7 std::cout << "x is odd\n";
8 break;
9 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 134
The switch Statement: Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 135
The switch Statement: Example (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 136
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–2019 Michael D. Adams C++ Version: 2019-02-04 137
The while Statement: Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 138
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–2019 Michael D. Adams C++ Version: 2019-02-04 139
The for Statement (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 140
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–2019 Michael D. Adams C++ Version: 2019-02-04 141
Range-Based for Statement
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 142
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–2019 Michael D. Adams C++ Version: 2019-02-04 143
The do Statement: Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 144
The break Statement
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 145
The continue Statement
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 146
The goto Statement
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 147
Section 2.3.5
Functions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 148
Function Parameters, Arguments, and Return Values
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 149
Function Declarations and Definitions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 150
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–2019 Michael D. Adams C++ Version: 2019-02-04 151
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–2019 Michael D. Adams C++ Version: 2019-02-04 152
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–2019 Michael D. Adams C++ Version: 2019-02-04 153
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–2019 Michael D. Adams C++ Version: 2019-02-04 154
The main Function
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 156
Lifetime
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 157
Parameter Passing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 158
Pass-By-Value Versus Pass-By-Reference
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 159
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–2019 Michael D. Adams C++ Version: 2019-02-04 160
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–2019 Michael D. Adams C++ Version: 2019-02-04 161
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–2019 Michael D. Adams C++ Version: 2019-02-04 162
String Length Example: Not Const Correct
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 163
String Length Example: Const Correct
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 164
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–2019 Michael D. Adams C++ Version: 2019-02-04 165
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–2019 Michael D. Adams C++ Version: 2019-02-04 166
Function Types and the const Qualifier
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 167
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 ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 10.1.6/6]
■ example:
inline bool isEven(int x) {
return x % 2 == 0;
}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 168
Inlining of a Function
■ Code fragment 2:
void myFunction() {
int i = 3;
bool result = (i % 2 == 0);
}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 169
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 ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 10.1.5/1]
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–2019 Michael D. Adams C++ Version: 2019-02-04 171
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–2019 Michael D. Adams C++ Version: 2019-02-04 172
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–2019 Michael D. Adams C++ Version: 2019-02-04 173
Compile-Time Versus Run-Time Computation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 174
Function Overloading
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 175
Default Arguments
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 176
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: [C++17 16.3.3.1]
⁓⁓⁓⁓⁓⁓⁓⁓
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–2019 Michael D. Adams C++ Version: 2019-02-04 178
The assert Macro
■ assert macro allows testing of boolean condition at run time
■ typically used to test sanity of code (e.g., test preconditions,
postconditions, or other invariants) or test validity of assumptions made by
code
■ defined in header file cassert
■ macro takes single argument: boolean expression
■ if assertion fails, program is terminated by calling std::abort
■ if NDEBUG preprocessor symbol is defined at time cassert header file
included, all assertions are disabled (i.e., not checked)
■ assert(expr) is constant expression if expr is constant expression that
evaluates to true or NDEBUG is defined ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 22.3.2/1]
■ example:
#include <cassert>
double sqrt(double x) {
assert(x >= 0);
// ...
}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 179
Section 2.3.6
Input/Output (I/O)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 180
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–2019 Michael D. Adams C++ Version: 2019-02-04 182
I/O Manipulators
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 183
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–2019 Michael D. Adams C++ Version: 2019-02-04 184
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–2019 Michael D. Adams C++ Version: 2019-02-04 185
Use of std::istream::eof
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 188
Use of std::endl
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 189
Use of std::endl (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 190
Stream Extraction Failure
■ for built-in types, if stream extraction fails, value of target for stream
extraction depends on reason for failure ⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 25.4.2.1.2/3 (Stage 3)]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 192
Testing Failure State of Streams
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 193
Section 2.3.7
Miscellany
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 194
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–2019 Michael D. Adams C++ Version: 2019-02-04 195
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–2019 Michael D. Adams C++ Version: 2019-02-04 196
Nested Namespace Definitions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 197
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–2019 Michael D. Adams C++ Version: 2019-02-04 198
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–2019 Michael D. Adams C++ Version: 2019-02-04 199
Unnamed Namespaces
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 200
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–2019 Michael D. Adams C++ Version: 2019-02-04 201
User-Defined Literals
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 202
Attributes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 203
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–2019 Michael D. Adams C++ Version: 2019-02-04 204
Some GCC and Clang Attributes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 205
Section 2.3.8
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 206
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 207
Section 2.4
Classes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 208
Classes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 209
Section 2.4.1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 210
Class Members
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 211
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–2019 Michael D. Adams C++ Version: 2019-02-04 212
Class Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 213
Default Member Access
class Widget {
// ...
};
class Widget {
private:
// ...
};
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 214
The struct Keyword
struct Widget {
// ...
};
class Widget {
public:
// ...
};
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 215
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–2019 Michael D. Adams C++ Version: 2019-02-04 216
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–2019 Michael D. Adams C++ Version: 2019-02-04 217
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–2019 Michael D. Adams C++ Version: 2019-02-04 218
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–2019 Michael D. Adams C++ Version: 2019-02-04 219
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–2019 Michael D. Adams C++ Version: 2019-02-04 220
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–2019 Michael D. Adams C++ Version: 2019-02-04 221
Friends
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 223
Section 2.4.2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 224
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–2019 Michael D. Adams C++ Version: 2019-02-04 225
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.
■ example:
class Vector { // Two-dimensional vector class.
public:
Vector() // Default constructor.
{x_ = 0.0; y_ = 0.0;}
// ...
private:
double x_; // The x component of the vector.
double y_; // The y component of the vector.
};
Vector v; // calls Vector::Vector(); v set to (0,0)
Vector x(); // declares function x that returns Vector
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 228
Defaulted Default Constructor
T::T() {}
■ if class has no default member initializers, this corresponds to default
constructing each data member of class type and leaving data members
of built-in type uninitialized
■ defaulted default constructor automatically provided (i.e., implicitly
declared) as public member if no user-declared constructors ⁓⁓⁓⁓⁓⁓⁓
[C++17 15.1/4]
■ example:
#include <string>
// class has implicitly-defined defaulted
// default constructor
struct Widget {
void foo() {}
std::string s;
};
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 229
Copy Constructor
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 230
Defaulted Copy Constructor
2 move constructor
2 move assignment operator
2 copy assignment operator (if not relying on deprecated behavior)
2 destructor (if not relying on deprecated behavior)
■ example:
// class has defaulted copy constructor
class Widget {
public:
Widget(int i) {i_ = i;}
int get() const {return i_;}
private:
int i_;
};
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 231
Move Constructor
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 232
Defaulted Move Constructor
2 copy constructor
2 copy assignment operator
2 move assignment operator
2 destructor
■ example:
// class has defaulted move constructor
struct Widget {
Widget();
void foo();
};
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 233
Constructor Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 234
Constructor Example (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 235
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–2019 Michael D. Adams C++ Version: 2019-02-04 236
Constructor Initializer Lists
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 237
Constructor Initializer List Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 238
Default Member Initializers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 239
Member Initialization Order
■ recall that data members initialized in order of declaration in class
definition
■ failing to consider this fact can easily leads to bugs in code
■ for example, consider following code:
1 #include <cassert>
2
3 class Widget {
4 public:
5 Widget() : y_(42), x_(y_ + 1) {assert(x_ == 43);}
6 int x_;
7 int y_;
8 };
9
10 int main() {
11 Widget w;
12 }
■ in Widget’s default constructor, x_ initialized before y_, which results in
use of y_ before its initialization
■ therefore, above code has undefined behavior
■ in practice, likely x_ will simply have garbage value when body of
constructor executes and assertion will fail
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 240
Destructors
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 241
Defaulted Destructor
■ for classes that require additional clean-up, defaulted destructor will not
yield correct behavior
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 242
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–2019 Michael D. Adams C++ Version: 2019-02-04 243
Section 2.4.3
Operator Overloading
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 244
Operator Overloading
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 245
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–2019 Michael D. Adams C++ Version: 2019-02-04 246
Operator Overloading (Continued 2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 247
Operator Overloading (Continued 3)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 248
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–2019 Michael D. Adams C++ Version: 2019-02-04 249
Operator Overloading Example: Array10
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 250
Operator Overloading: Member vs. Nonmember Functions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 251
Overloading as Member vs. Nonmember: Example
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 // Alternatively, overload operator+ as a member function.
7 // Complex operator+(double b) const
8 // {return Complex(real() + b, imag());}
9 private:
10 double x_; // The real part.
11 double y_; // The imaginary part.
12 };
13
14 // Overload as a nonmember function.
15 // (A member function could instead be used. See above.)
16 Complex operator+(const Complex& a, double b)
17 {return Complex(a.real() + b, a.imag());}
18
19 // This can only be accomplished with a nonmember function.
20 Complex operator+(double b, const Complex& a)
21 {return Complex(b + a.real(), a.imag());}
22
23 void myFunc() {
24 Complex a(1.0, 2.0);
25 Complex b(1.0, -2.0);
26 double r = 2.0;
27 Complex c = a + r; /* could use nonmember or member function
28 operator+(a, r) or a.operator+(r) */
29 Complex d = r + a; /* must use nonmember function
30 operator+(r, a), since r.operator+(a) will not work */
31 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 252
Overloading as Member vs. Nonmember: Example
1 #include <string_view>
2
3 class Widget {
4 public:
5 Widget();
6 Widget(std::string_view); // converting constructor
7 operator std::string_view() const; // conversion operator
8 // ...
9 };
10
11 // overload as nonmember function
12 Widget operator+(Widget, std::string_view);
13
14 int main() {
15 Widget w;
16 std::string_view sv("hello");
17 Widget a = w + sv;
18 /* OK: operator+(Widget, std::string_view) called
19 with no conversions necessary */
20 Widget b = sv + w;
21 /* OK: operator+(Widget, std::string_view) called, where
22 first argument implicitly converted to Widget by
23 Widget’s converting constructor and second argument
24 implicitly converted to std::string_view by
25 Widget’s conversion operator; if operator+ were
26 overloaded as member of Widget class, compiler error
27 would result as overload resolution would fail to
28 yield any viable function to call */
29 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 253
Copy Assignment Operator
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 254
Defaulted Copy Assignment Operator
■ defaulted copy assignment operator performs memberwise copy of its
data members (and bases), where copy performed using: [C++17 15.8.2/12]
⁓⁓⁓⁓⁓⁓⁓⁓
2 copy assignment operator for class types
2 bitwise copy for built-in types
■ defaulted copy assignment operator automatically provided (i.e., implicitly
defined) as public member if none of following user declared: [C++17 15.8.2/2]
⁓⁓⁓⁓⁓⁓⁓⁓
2 move constructor
2 move assignment operator
2 copy constructor (if not relying on deprecated behavior)
2 destructor (if not relying on deprecated behavior)
■ example:
// class has implicitly-defined defaulted
// copy-assignment operator
class Widget {
public:
Widget(int i) {i_ = i;}
int get() const {return i_;}
private:
int i_;
};
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 255
Self-Assignment Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 256
Move Assignment Operator
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 257
Defaulted Move Assignment Operator
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 259
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–2019 Michael D. Adams C++ Version: 2019-02-04 261
std::initializer_list Class Template
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 262
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–2019 Michael D. Adams C++ Version: 2019-02-04 263
Converting Constructors
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 264
Explicit Constructors
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 265
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–2019 Michael D. Adams C++ Version: 2019-02-04 266
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–2019 Michael D. Adams C++ Version: 2019-02-04 267
Conversion Operators
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 270
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:
1 class Widget {
2 public:
3 Widget(char c, int i) : c_(c), i_(i) {}
4 Widget(int i) : Widget(’a’, i) {}
5 // delegating constructor
6 // ...
7 private:
8 char c_;
9 int i_;
10 };
11
12 int main() {
13 Widget w(’A’, 42);
14 Widget v(42);
15 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 271
Static Data Members
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 272
Static Member Functions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 273
constexpr Member Functions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 274
constexpr Constructors
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 275
Example: Constexpr Constructors and Member Functions
1 #include <iostream>
2
3 // Two-dimensional vector class.
4 class Vector {
5 public:
6 constexpr Vector() : x_(0), y_(0) {}
7 constexpr Vector(double x, double y) : x_(x), y_(y) {}
8 constexpr Vector(const Vector& v) : x_(v.x_), y_(v.y_) {}
9 constexpr Vector& operator=(const Vector& v)
10 {x_ = v.x_; y_ = v.y_; return *this;}
11 constexpr double x() const {return x_;}
12 constexpr double y() const {return y_;}
13 constexpr double squared_norm() const
14 {return x_ * x_ + y_ * y_;}
15 // ...
16 private:
17 double x_; // The x component of the vector.
18 double y_; // The y component of the vector.
19 };
20
21 int main() {
22 constexpr Vector v(3.0, 4.0);
23 static_assert(v.x() == 3.0 && v.y() == 4.0);
24 constexpr double d = v.squared_norm();
25 std::cout << d << ’\n’;
26 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 276
Why Constexpr Member Functions Are Not Implicitly Const
1 class Widget {
2 public:
3 constexpr Widget() : i_(42) {}
4 constexpr const int& get() const {return i_;}
5 constexpr int& get() /* what if implicitly const? */
6 {return i_;}
7 // ...
8 private:
9 int i_;
10 };
11
12 constexpr Widget w;
13 static_assert(w.get() == 42);
14 // invokes const member function
15 constexpr int i = ++Widget().get();
16 // invokes non-const member function
17 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–2019 Michael D. Adams C++ Version: 2019-02-04 277
The mutable Qualifier
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 278
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–2019 Michael D. Adams C++ Version: 2019-02-04 279
Pointers to Members
■ pointer to member provides means to reference particular nonstatic
member of class, independent of any class object instance
■ pointer to member can only be formed for nonstatic (data or function)
member of class
■ can obtain pointer to member that references nonstatic member m in class
T by applying address-of operator to T::m (i.e., using expression &T::m)
■ special value nullptr can be given to pointer to member to indicate
that pointer to member does not refer to any member
■ pointer to member of class T written as T::*
■ type of pointer to member embodies type of class and type of member
within class
■ example:
2 int Widget::* is pointer to member of Widget class having type int
2 const int Widget::* is pointer to member of Widget class having
of Gadget class that takes single int parameter and has return type of
float
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 280
Pointers to Members (Continued)
■ since pointer to member is not associated with any class object instance,
dereferencing pointer to member requires object (or pointer to object) to
be specified
■ given object x of type T, can access member through pointer to member
ptm by applying member-selection operator .* to x using expression
x.*ptm
■ given pointer p to object of type T, can access member through pointer to
member ptm by applying member-selection operator ->* to p using
expression p->*ptm
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 281
Pointers to Members for Data Members
■ conceptually, pointer to member for (nonstatic) data member can be
thought of as offset (in memory) from start of class object to start of data
member (i.e., location of data member relative to start of class object)
■ since pointer to member does not identify particular object instance (i.e.,
value for this), pointer to member alone not sufficient to specify
particular instance of member in object
■ consequently, when dereferencing pointer to member, must always
specify object (or pointer to object)
■ example:
1 struct Widget {
2 int i;
3 inline static int j;
4 };
5
6 int main(){
7 Widget w, v;
8 int Widget::* ptm = &Widget::i; // pointer to member
9 int* jp = &Widget::j;
10 // address of static member is ordinary pointer
11 w.*ptm = 42; // w.*ptm references w.i
12 v.*ptm = 42; // v.*ptm references v.i
13 *jp = 42; // references Widget::j
14 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 282
Pointers to Members and Const Example
1 #include <type_traits>
2
3 template <class T1, class T2, class T3>
4 struct triplet {
5 triplet(T1 first_, T2 second_, T3 third_) :
6 first(first_), second(second_), third(third_) {}
7 T1 first;
8 T2 second;
9 T3 third;
10 };
11
12 int main() {
13 using widget = triplet<const int, int, double>;
14 widget w(1, 1, 1.0);
15 static_assert(std::is_same_v<decltype(&widget::first),
16 const int widget::*>);
17 static_assert(std::is_same_v<decltype(&widget::second),
18 int widget::*>);
19 static_assert(std::is_same_v<decltype(&widget::third),
20 double widget::*>);
21 const int widget::* cp = nullptr;
22 int widget::* p = nullptr;
23 cp = &widget::first; // OK: constness of pointee same
24 cp = &widget::second; // OK: adds const to pointee
25 // p = &widget::first; // ERROR: discards const from pointee
26 p = &widget::second; // OK: constness of pointee same
27 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 283
Pointers to Members for Function Members
■ pointer to member for (nonstatic) member function simply identifies
particular member function of class (independent of any object instance)
■ since pointer to member does not identify particular object instance (i.e.,
value for this), pointer to member alone not sufficient to invoke member
function
■ consequently, when dereferencing pointer to member, must always
specify object (or pointer to object) so that this parameter can be set
appropriately
■ example:
1 struct Widget {
2 void func() {/* ... */}
3 static void set_verbosity(int level) {/* ... */}
4 };
5
6 int main() {
7 Widget w, v;
8 void (Widget::* ptm)() = &Widget::func; // pointer to member
9 void (*pf)(int) = &Widget::set_verbosity;
10 // address of static member is ordinary pointer
11 (w.*ptm)(); // calls w.func()
12 (v.*ptm)(); // calls v.func()
13 (*pf)(42); // calls Widget::set_verbosity()
14 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 284
Pointers to Members: Example
1 #include <string>
2 #include <cassert>
3
4 struct Widget {
5 std::string s;
6 int i = 0;
7 void clear() {i = 0; s = "";}
8 };
9
10 int main() {
11 Widget w;
12 Widget* wp = &w;
13
14 // pointer to member of Widget of type int
15 int Widget::* iptm = nullptr;
16 // w.*iptm = 42; // ERROR: null pointer to member
17 iptm = &Widget::i; // iptm references i member of Widget
18 w.*iptm = 42; // w.*iptm references w.i
19 assert(w.i == 42);
20
21 // pointer to member of Widget of type std::string
22 std::string Widget::* sptm = &Widget::s;
23 wp->*sptm = "hello"; // wp->*sptm references w.s
24 assert(w.s == "hello");
25
26 // pointer to member of Widget that is function that takes
27 // no parameters and returns void
28 void (Widget::* fptm)() = &Widget::clear;
29 (w.*fptm)(); // w.*fptm references w.clear
30 assert(w.i == 0 && w.s == "");
31 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 285
Pointers to Members: Example
1 #include <iostream>
2
3 class Widget {
4 public:
5 Widget(bool flag) {
6 op_ = flag ? &Widget::op_2 : &Widget::op_1;
7 }
8 void modify() {
9 // ...
10 (this->*op_)(); // invoke member function
11 // ...
12 }
13 // ...
14 private:
15 void op_1() {std::cout << "op_1 called\n";}
16 void op_2() {std::cout << "op_2 called\n";}
17 void (Widget::*op_)();
18 // pointer to member function of Widget class that
19 // takes no parameters and returns no value
20 // ...
21 };
22
23 int main() {
24 Widget u(false);
25 Widget v(true);
26 u.modify(); // modify invokes op_1
27 v.modify(); // modify invokes op_2
28 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 286
Pointers to Members Example: Statistics Calculation
1 #include <iostream>
2
3 template <auto Count, auto Sum, class T, class Value>
4 void update_statistics(T& stats, Value value) {
5 ++(stats.*Count); // adjust count of values
6 stats.*Sum += value; // adjust sum of values
7 }
8
9 struct Widget {
10 int count = 0; // count
11 double sum = 0; // sum
12 short int si;
13 };
14
15 struct Gadget {
16 int n = 0; // count
17 double d;
18 double sigma = 0; // sum
19 };
20
21 int main() {
22 Widget w;
23 Gadget g;
24 for (auto&& x : {0.5, 1.5, 2.5}) {
25 update_statistics<&Widget::count, &Widget::sum>(w, x);
26 update_statistics<&Gadget::n, &Gadget::sigma>(g, x);
27 }
28 std::cout << w.sum / static_cast<double>(w.count) << ’\n’;
29 std::cout << g.sigma / static_cast<double>(g.n) << ’\n’;
30 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 287
Stream Inserters
■ inserter and extractor should use compatible formats (i.e., what is written
by inserter should be readable by extractor)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 288
Stream Extractors
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 289
Structured Bindings
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 290
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::tuple(true, 42, ’A’);
20 auto [tb, ti, tc] = t;
21 assert(tb == true && ti == 42 && tc == ’A’);
22 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 291
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–2019 Michael D. Adams C++ Version: 2019-02-04 292
Literal Types
1 // literal type
2 class Widget {
3 public:
4 constexpr Widget(int i = 0) : i_(i) {}
5 ˜Widget() = default; // trivial destructor
6 private:
7 int i_;
8 };
9
10 // not literal type
11 class Gadget {
12 public:
13 constexpr Gadget() {}
14 ˜Gadget() {} // non-trivial destructor
15 };
16
17 // not literal type
18 // no constexpr constructor, excluding copy/move constructor
19 class Foo {
20 public:
21 Foo() {};
22 ˜Foo() = default; // trivial destructor
23 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 294
Constexpr Variable Requirements
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 295
Example: Constexpr Variable Requirement Violations
1 #include <string>
2
3 constexpr std::string s("hello");
4 // ERROR: not literal type
5
6 constexpr int i;
7 // ERROR: not initialized
8
9 float func();
10 constexpr float f = 2.0 * func();
11 // ERROR: initializer expression not constant expression
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 296
Constexpr Function Requirements
[C++17 10.1.5/5]
⁓⁓⁓⁓⁓⁓⁓⁓
2 must not be virtual
2 its return type must be literal type
2 each of its parameters must be of literal type
2 there exists at least one set of argument values such that invocation of
function could be evaluated expression of core constant expression
2 function body must be either deleted or defaulted or contain any statements
except:
2 asm declaration
2 goto statement
2 statement with label other than case and default
2 try block
2 definition of variable of non-literal type
2 definition of variable of static or thread storage duration
2 definition of variable for which no initialization is performed
2 if function is defaulted copy/move assignment, class of which it is member
must not have mutable member
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 297
Example: Constexpr Function Requirement Violations
1 #include <vector>
2 #include <string>
3 #include <iostream>
4
5 // ERROR: return type not literal type
6 constexpr std::vector<int> get_value()
7 {return std::vector<int>{1, 2, 3};}
8
9 // ERROR: parameter type not literal type
10 constexpr void foo(std::string s) { /* ... */ }
11
12 // ERROR: no argument exists such that function can be used
13 // in constant expression
14 constexpr void output(int i) {std::cout << i << ’\n’;}
15
16 constexpr void func() {
17 int i; // ERROR: variable not initialized
18 std::vector<int> v{1, 2, 3};
19 // ERROR: definition of variable of non-literal type
20 // ...
21 }
22
23 constexpr int count() {
24 static unsigned int i = 0;
25 // ERROR: definition of variable with static storage
26 // duration
27 return i++;
28 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 298
Constexpr Constructor Requirements
[C++17 10.1.5/5]
⁓⁓⁓⁓⁓⁓⁓⁓
2 each of its parameters must be of literal type
2 class must not have any virtual base classes
2 constructor must not have function try block
2 constructor body must be either deleted or defaulted or satisfy following
constraints:
2 compound statement of constructor body must satisfy constraints for body of
constexpr function
2 every base class sub-object and every non-static data member must be
initialized
2 every constructor selected to initialize non-static members and base class
must be constexpr constructor
2 if constructor is defaulted copy/move constructor, class of which it is
member must not have mutable member
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 299
Example: Constexpr Constructor Requirement Violations
1 #include <string>
2
3 class Widget {
4 public:
5 constexpr Widget() {}
6 // ERROR: i_ not initialized
7 constexpr Widget(std::string s);
8 // ERROR: parameter type not literal
9 // ...
10 private:
11 int i_;
12 };
13
14 // OK
15 class Base {
16 public:
17 Base(int i) : i_(i) {}
18 private:
19 int i_;
20 };
21
22 class Derived : public Base {
23 public:
24 constexpr Derived() : Base(42) {}
25 // ERROR: Base constructor not constexpr
26 // ...
27 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 300
Example: Pointers/References to Constexpr Objects
1 class Buffer {
2 public:
3 constexpr Buffer() : data_() {}
4 constexpr const char& operator[](unsigned int i) const
5 {return data_[i];}
6 constexpr char& operator[](unsigned int i)
7 {return data_[i];}
8 constexpr const char* data() const {return data_;}
9 // ...
10 private:
11 char data_[256];
12 };
13
14 int main() {
15 constexpr Buffer b; // OK
16 constexpr Buffer a = b; // OK
17 constexpr char c = b[0]; // OK
18 // constexpr const Buffer& br = b;
19 // ERROR: reference to b is not a constant expression
20 // constexpr const char& cr = b[0];
21 // ERROR: reference to subobject of b is not constant
22 // expression
23 // constexpr const char* cp = b.data();
24 // ERROR: pointer to subobject of b is not constant
25 // expression
26 // constexpr const Buffer* bp = &b;
27 // ERROR: pointer to b is not constant expression
28 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 301
Example: Constexpr and Accessing External State
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 302
Example: Constexpr and Immediate Initialization
1 #include <iostream>
2 #include <cstddef>
3
4 template <class T, std::size_t Size>
5 class Buffer {
6 public:
7 // ERROR: data_ data member is constructed without
8 // being (immediately) initialized
9 // constexpr Buffer() {
10 // for (std::size_t i = 0; i < size(); ++i)
11 // {data_[i] = 0;}
12 // }
13 // OK: all data members is initialized when constructed
14 // (array data_ is initialized to all zero)
15 constexpr Buffer() : data_{} {}
16 static constexpr std::size_t size() {return Size;}
17 const T& operator[](std::size_t i) const {return data_[i];}
18 // ...
19 private:
20 T data_[size()];
21 };
22
23 int main() {
24 constexpr Buffer<char, 16> b;
25 for (int i = 0; i < 16; ++i)
26 {std::cout << static_cast<int>(b[i]) << ’\n’;}
27 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 303
Debugging Constexpr Functions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 304
Example: Debugging Strategies for Constexpr Functions
1 #include <stdexcept>
2 #include <cassert>
3
4 constexpr double sqrt(double x) {
5 // if assertion fails, sqrt function will not yield
6 // constant expression
7 assert(x >= 0.0);
8 double result = 0.0;
9 // ... (correctly initialize result)
10 return result;
11 }
12
13 constexpr int foo(unsigned x) {
14 unsigned i = 0;
15 // ... (code that changes i)
16 // assume odd i indicative of bug
17 // if i is odd (which would result in exception
18 // being thrown), foo function will not yield
19 // constant expression
20 if (i & 1) {throw std::logic_error("i is odd");}
21 return 0;
22 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 305
Proxy Classes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 306
Proxy Class Example: BoolVector
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 307
Proxy Class Example: BoolVector.hpp
1 #include <cstddef>
2 #include <utility>
3
4 class BoolVector;
5
6 class Proxy {
7 public:
8 ˜Proxy() = default;
9 Proxy& operator=(const Proxy&);
10 Proxy& operator=(bool b);
11 operator bool() const;
12 private:
13 friend class BoolVector;
14 Proxy(const Proxy&) = default;
15 Proxy(BoolVector* v, std::size_t i) : v_(v), i_(i) {}
16 BoolVector* v_;
17 std::size_t i_;
18 };
19
20 class BoolVector {
21 public:
22 BoolVector(std::size_t n) : n_(n), d_(new unsigned char[(n + 7) / 8]) {std::fill_n(d_, (n + 7) / 8, 0);}
23 ˜BoolVector() {delete [] d_;}
24 std::size_t size() const {return n_;}
25 bool operator[](std::size_t i) const {return getElem(i);}
26 Proxy operator[](std::size_t i) {return Proxy(this, i);}
27 private:
28 friend class Proxy;
29 bool getElem(std::size_t i) const {return (d_[i / 8] >> (i % 8)) & 1;}
30 void setElem(std::size_t i, bool b) {(d_[i / 8] &= ˜(1 << (i % 8))) |= (b << (i % 8));}
31 std::size_t n_;
32 unsigned char* d_;
33 };
34
35 inline Proxy& Proxy::operator=(const Proxy& other) {v_->setElem(i_, other); return *this;}
36 inline Proxy& Proxy::operator=(bool b) {v_->setElem(i_, b); return *this;}
37 inline Proxy::operator bool() const {return v_->getElem(i_);}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 308
Proxy Class Example: BoolVector.cpp
1 #include <cassert>
2 #include <iostream>
3 #include "BoolVector.hpp"
4
5 int main() {
6 constexpr int bits[] = {0, 0, 1, 1, 0, 1, 0, 1};
7 constexpr int n = sizeof(bits) / sizeof(int);
8 BoolVector v(n);
9 BoolVector w(n);
10 assert(v.size() == n && w.size() == n);
11 for (int i = 0; i < n; ++i) {
12 w[i] = v[i] = bits[i];
13 }
14 const BoolVector& cv = v;
15 for (int i = 0; i < n; ++i) {
16 assert(v[i] == bits[i]);
17 assert(w[i] == bits[i]);
18 assert(cv[i] == bits[i]);
19 std::cout << (v[i] ? ’1’ : ’0’);
20 }
21 std::cout << ’\n’;
22 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 309
Implementing Postfix Increment/Decrement Operator
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 310
Pointer-to-Implementation (Pimpl) Idiom
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 311
Pimpl and std::experimental::propagate_const
1 #include <experimental/propagate_const>
2 #include <memory>
3 #include <iostream>
4
5 class WidgetImpl {
6 public:
7 void foo() {std::cout << "WidgetImpl::foo()\n";}
8 void foo() const {std::cout << "WidgetImpl::foo() const\n";}
9 };
10
11 class Widget {
12 public:
13 void foo() {p_->foo();}
14 void foo() const {p_->foo();}
15 private:
16 std::experimental::propagate_const<std::unique_ptr<WidgetImpl>> p_;
17 /* const or non-const member functions of WidgetImpl invoked
18 as appropriate based on constness of *this; using
19 std::unique_ptr<const WidgetImpl> would cause only const member
20 functions of WidgetImpl to be invoked; using
21 std::unique_ptr<WidgetImpl> would cause only non-const member
22 functions of WidgetImpl to be invoked */
23 };
24
25 int main() {
26 Widget w;
27 const Widget cw;
28 w.foo(); // calls WidgetImpl:foo()
29 cw.foo(); // calls WidgetImpl:foo() const
30 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 312
Section 2.4.5
Functors
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 313
Functors
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 315
Functor Example With State
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 316
Ordering Relations
■ note: “¬” denotes logical NOT, “∧” denotes logical AND, and “∨” denotes
logical OR
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 317
Selection Sort Example
1 #include <algorithm>
2 #include <cassert>
3 #include <forward_list>
4 #include <functional>
5
6 // reverse digits in decimal representation of integer
7 constexpr unsigned int reverse(unsigned int x) {
8 unsigned int y = 0;
9 for (; x; x /= 10)
10 {auto d = x % 10; x -= d; y = 10 * y + d;}
11 return y;
12 }
13
14 constexpr bool rev_less(unsigned int x, unsigned int y)
15 {return reverse(x) < reverse(y);}
16
17 template <class ForwardIterator, class Compare>
18 void selection_sort(ForwardIterator first, ForwardIterator last, Compare less) {
19 for (auto i = first; i != last; ++i)
20 {std::iter_swap(i, std::min_element(i, last, less));}
21 }
22
23 int main() {
24 std::forward_list<unsigned int> values{12, 21, 123, 321, 1234, 4321};
25 selection_sort(values.begin(), values.end(), std::greater<unsigned int>());
26 assert((values == std::forward_list<unsigned int>{
27 4321, 1234, 321, 123, 21, 12}));
28 selection_sort(values.begin(), values.end(), rev_less);
29 assert((values == std::forward_list<unsigned int>{
30 21, 12, 321, 123, 4321, 1234}));
31 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 318
Bubble Sort Example
1 #include <algorithm>
2 #include <cassert>
3 #include <forward_list>
4 #include <functional>
5
6 template <class ForwardIterator, class Compare>
7 void bubble_sort(ForwardIterator first, ForwardIterator last, Compare less) {
8 for (auto sorted = first; first != last; last = sorted) {
9 sorted = first;
10 for (auto cur = first, prev = first; ++cur != last; ++prev) {
11 if (less(*cur, *prev)) {
12 std::iter_swap(cur, prev);
13 sorted = cur;
14 }
15 }
16 }
17 }
18
19 int main() {
20 std::forward_list<int> values{7, 0, 6, 1, 5, 2, 4, 3};
21 bubble_sort(values.begin(), values.end(), std::less<int>());
22 assert((values == std::forward_list<int>{0, 1, 2, 3, 4, 5, 6, 7}));
23 bubble_sort(values.begin(), values.end(), std::greater<int>());
24 assert((values == std::forward_list<int>{7, 6, 5, 4, 3, 2, 1, 0}));
25 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 319
Comparison Object Example
1 #include <cassert>
2 #include <algorithm>
3 #include <set>
4
5 template <class T> class compare {
6 public:
7 constexpr compare(bool less = true) : less_(less) {}
8 constexpr bool operator()(const T& x, const T& y) const
9 {return less_ ? (x < y) : (x > y);}
10 private:
11 bool less_;
12 };
13
14 constexpr bool even_then_odd(int x, int y)
15 {if ((x % 2) != (y % 2)) {return !(x % 2);} else {return x < y;}}
16
17 int main() {
18 constexpr int values[] = {0, 7, 6, 1, 2, 5, 3, 4};
19 std::set<int, compare<int>> s1(std::begin(values), std::end(values));
20 constexpr int d1[] = {0, 1, 2, 3, 4, 5, 6, 7};
21 assert(std::equal(s1.begin(), s1.end(), std::begin(d1)));
22 std::set<int, compare<int>> s2(std::begin(values), std::end(values),
23 compare<int>(false));
24 constexpr int d2[] = {7, 6, 5, 4, 3, 2, 1, 0};
25 assert(std::equal(s2.begin(), s2.end(), std::begin(d2)));
26 std::set<int, bool (*)(int, int)> s3(std::begin(values), std::end(values),
27 even_then_odd);
28 constexpr int d3[] = {0, 2, 4, 6, 1, 3, 5, 7};
29 assert(std::equal(s3.begin(), s3.end(), std::begin(d3)));
30 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 320
Comparison Object Propagation
■ invariant of ordered container: elements of container always sorted with
respect to ordering relation defined by comparison object
■ thus, state for ordered container (ignoring possible allocator) consists of:
1 elements in container; and
2 comparison object that determines order of those elements
■ consider propagating value of one container to another (via copy or move)
■ when propagating value of container, two choices possible:
1 propagate comparison object
2 do not propagate comparison object
■ if comparison object not propagated and source and destination
comparison objects differ, must re-sort elements (to be consistent with
destination comparison object) to avoid violating container invariant
■ if no equality/inequality operator provided by comparison-object type,
must assume worst (i.e., not equal) and always re-sort
■ if comparison object propagated, never any need to re-sort elements
■ for efficiency, prefer solution of always propagating comparison object
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 321
Comparison Object Propagation Example
1 #include <algorithm>
2 #include <cassert>
3 #include <set>
4 #include <utility>
5
6 template <class T> class compare {
7 public:
8 compare(bool less = true) : less_(less) {}
9 bool operator()(const T& x, const T& y) const
10 {return less_ ? (x < y) : (x > y);}
11 bool less() const {return less_;}
12 private:
13 bool less_;
14 };
15
16 int main() {
17 constexpr int values[] = {0, 7, 6, 1, 2, 5, 3, 4};
18 std::set<int, compare<int>> s3(values, std::end(values));
19 std::set<int, compare<int>> s1(std::move(s3)); // move construct
20 assert(s1.key_comp().less()); // comparison object was moved
21 std::set<int, compare<int>> s2(s1); // copy construct
22 assert(s2.key_comp().less()); // comparison object was copied
23 s3 = std::set<int, compare<int>>(values, std::end(values),
24 compare<int>(false));
25 assert(s1.key_comp().less() && !s3.key_comp().less());
26 s1 = std::move(s3); // move assign
27 assert(!s1.key_comp().less()); // comparison object was moved
28 assert(s2.key_comp().less() && !s1.key_comp().less());
29 s2 = s1; // copy assign
30 assert(!s2.key_comp().less()); // comparison object was copied
31 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 322
Section 2.4.6
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 323
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 324
Section 2.5
Templates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 325
Templates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 326
Section 2.5.1
Function Templates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 327
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–2019 Michael D. Adams C++ Version: 2019-02-04 328
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
■ 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–2019 Michael D. Adams C++ Version: 2019-02-04 329
Function Templates (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 330
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–2019 Michael D. Adams C++ Version: 2019-02-04 331
Template Function Overload Resolution
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 332
Qualified Names
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 333
Dependent Names
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 334
Qualified Dependent Names
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 335
Why typename is Needed
1 int x = 42;
2
3 template <class T> void func() {
4 /* The compiler must be able to check the syntactic
5 correctness of this template code without knowing the
6 type T. Without knowing the type T, however, the meaning
7 of the following line of code is ambiguous, unless the
8 compiler follows some fixed rule for resolving this
9 ambiguity. In particular, is this line of code a
10 declaration of a variable x or an expression consisting
11 of a binary operator* with operands T::foo and x? */
12 T::foo* x; // Does T::foo name a type or an object?
13 }
14
15 struct ContainsType {
16 using foo = int; // foo is type
17 };
18
19 struct ContainsValue {
20 static int foo; // foo is value
21 };
22
23 int main() {
24 // Only one of the following two lines should be valid.
25 func<ContainsValue>();
26 func<ContainsType>();
27 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 336
Section 2.5.2
Class Templates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 337
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–2019 Michael D. Adams C++ Version: 2019-02-04 338
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–2019 Michael D. Adams C++ Version: 2019-02-04 339
Class Templates (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 340
Class Template Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 341
Class-Template Default Parameters
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 342
Qualified Dependent Names Revisited
■ recall, 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–2019 Michael D. Adams C++ Version: 2019-02-04 343
Why template is Needed
1 template<bool> struct Widget;
2
3 template<bool B> struct Gadget {
4 static int g() {
5 /* The compiler must be able to check the syntactic
6 correctness of this template code without knowing the
7 value of B. Without knowing the value of B, however,
8 the meaning of the following line of code is ambiguous,
9 unless the compiler follows some fixed rule for
10 resolving this ambiguity. In particular, is this line
11 of code using a data member called f and evaluating
12 (f < 0 > 42) or is it calling a template member
13 function called f with the argument 42? */
14 return Widget<B>::f<0>(42);
15 }
16 };
17
18 template<bool B> struct Widget {
19 template<int I> static int f(int i) {return i + I;}
20 };
21
22 template<> struct Widget<false> {inline static int f = 42;};
23
24 int main() {
25 // Only one of the following two lines should be valid.
26 Gadget<true>::g();
27 Gadget<false>::g();
28 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 344
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–2019 Michael D. Adams C++ Version: 2019-02-04 345
Class Template Parameter Deduction
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 346
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–2019 Michael D. Adams C++ Version: 2019-02-04 347
Template Deduction Guides
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 348
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–2019 Michael D. Adams C++ Version: 2019-02-04 349
Auto Non-Type Template Parameters
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 350
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–2019 Michael D. Adams C++ Version: 2019-02-04 351
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–2019 Michael D. Adams C++ Version: 2019-02-04 352
Section 2.5.3
Variable Templates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 353
Variable Templates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 354
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–2019 Michael D. Adams C++ Version: 2019-02-04 355
Section 2.5.4
Alias Templates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 356
Alias Templates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 357
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–2019 Michael D. Adams C++ Version: 2019-02-04 358
Section 2.5.5
Variadic Templates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 359
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–2019 Michael D. Adams C++ Version: 2019-02-04 360
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–2019 Michael D. Adams C++ Version: 2019-02-04 361
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 corresponds to zero or more
function parameters whose types correspond to elements of type
parameter pack Ts
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 362
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–2019 Michael D. Adams C++ Version: 2019-02-04 363
Parameter Pack Expansion
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 364
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–2019 Michael D. Adams C++ Version: 2019-02-04 365
Parameter Pack Expansion
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 366
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–2019 Michael D. Adams C++ Version: 2019-02-04 367
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–2019 Michael D. Adams C++ Version: 2019-02-04 368
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–2019 Michael D. Adams C++ Version: 2019-02-04 369
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–2019 Michael D. Adams C++ Version: 2019-02-04 370
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–2019 Michael D. Adams C++ Version: 2019-02-04 371
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–2019 Michael D. Adams C++ Version: 2019-02-04 372
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–2019 Michael D. Adams C++ Version: 2019-02-04 373
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)))
[C++17 17.5.3/9]
⁓⁓⁓⁓⁓⁓⁓⁓
■ unary fold of empty parameter pack: ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 17.5.3/9]
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–2019 Michael D. Adams C++ Version: 2019-02-04 375
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–2019 Michael D. Adams C++ Version: 2019-02-04 376
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–2019 Michael D. Adams C++ Version: 2019-02-04 377
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–2019 Michael D. Adams C++ Version: 2019-02-04 378
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–2019 Michael D. Adams C++ Version: 2019-02-04 379
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–2019 Michael D. Adams C++ Version: 2019-02-04 380
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–2019 Michael D. Adams C++ Version: 2019-02-04 381
Section 2.5.6
Template Specialization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 382
Template Specialization
■ sometimes can be desirable to provide customized version of template for
certain choices of template parameters
■ customized version of templates can be specified through language
feature known as template specialization
■ two kinds of specialization: explicit and partial
■ explicit specialization (less formally known as full specialization):
customized version of template where all template parameters are fixed
■ partial specialization: customized version of template where only some
of template parameters are fixed
■ class templates, function templates, and variable templates can all be
specialized
■ alias templates cannot be specialized
■ class templates and variable templates can be partially or explicitly
specialized
■ function templates can only be explicitly specialized (not partially)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 383
Explicit Specialization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 384
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–2019 Michael D. Adams C++ Version: 2019-02-04 385
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–2019 Michael D. Adams C++ Version: 2019-02-04 386
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 int main() {}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 387
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 version
23 Widget<int, double> w2; // partial specialization
24 Widget<int, int> w3; // explicit specialization
25 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 388
Partially-Specialized Class Template: std::vector
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 389
Explicitly-Specialized Variable Template: is_void_v
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 390
Explicitly-Specialized Variable Template: factorial
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 391
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() : std::numeric_limits<int>::max();
11
12 static_assert(quotient<4, 2> == 2);
13 static_assert(quotient<5, 3> == 1);
14 static_assert(quotient<4, 0> == std::numeric_limits<int>::max());
15 static_assert(quotient<-4, 0> == std::numeric_limits<int>::min());
16
17 int main() {}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 392
Section 2.5.7
Miscellany
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 393
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:
2 all types used in function type (i.e., return type and types of all parameters)
2 all types used in template parameter declarations
2 all expressions used in function type
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 394
Some Kinds of Substitution Failures
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 395
Some Kinds of Substitution Failures (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 396
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 }
[see overload
. . . . . . . . . .resolution]
..........
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 397
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–2019 Michael D. Adams C++ Version: 2019-02-04 398
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–2019 Michael D. Adams C++ Version: 2019-02-04 399
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–2019 Michael D. Adams C++ Version: 2019-02-04 400
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–2019 Michael D. Adams C++ Version: 2019-02-04 401
Section 2.5.8
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 402
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 403
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 404
Talks II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 405
Section 2.6
Lambda Expressions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 406
Motivation for Lambda Expressions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 407
Lambda Expressions
■ lambda expression consists of:
1 introducer: capture list in square brackets
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–2019 Michael D. Adams C++ Version: 2019-02-04 410
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–2019 Michael D. Adams C++ Version: 2019-02-04 411
Capturing Objects
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 412
std::transform
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 413
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–2019 Michael D. Adams C++ Version: 2019-02-04 414
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–2019 Michael D. Adams C++ Version: 2019-02-04 415
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–2019 Michael D. Adams C++ Version: 2019-02-04 416
std::for_each
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 417
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–2019 Michael D. Adams C++ Version: 2019-02-04 418
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–2019 Michael D. Adams C++ Version: 2019-02-04 419
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–2019 Michael D. Adams C++ Version: 2019-02-04 420
More Variations on Capture
double a = 2.14;
double b = 3.14;
double c = 42.0;
// capture all objects by reference (i.e., a, b, and c)
[&](double x, double y){return a * x + b * y + c;}
// capture all objects by value (i.e., a, b, and c)
[=](double x, double y){return a * x + b * y + c;}
// capture all objects by value, except a
// which is captured by reference
[=,&a](double x, double y){return a * x + b * y + c;}
// capture all objects by reference, except a
// which is captured by value
[&,a](double x, double y){return a * x + b * y + c;}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 421
Generalized Lambda Capture
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 422
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–2019 Michael D. Adams C++ Version: 2019-02-04 423
Generic Lambda Expressions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 424
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–2019 Michael D. Adams C++ Version: 2019-02-04 425
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–2019 Michael D. Adams C++ Version: 2019-02-04 426
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–2019 Michael D. Adams C++ Version: 2019-02-04 427
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–2019 Michael D. Adams C++ Version: 2019-02-04 428
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–2019 Michael D. Adams C++ Version: 2019-02-04 429
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–2019 Michael D. Adams C++ Version: 2019-02-04 430
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–2019 Michael D. Adams C++ Version: 2019-02-04 431
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–2019 Michael D. Adams C++ Version: 2019-02-04 432
Dangling References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 433
Section 2.6.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 434
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 435
Section 2.7
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 436
Section 2.7.1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 437
Derived Classes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 439
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–2019 Michael D. Adams C++ Version: 2019-02-04 440
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–2019 Michael D. Adams C++ Version: 2019-02-04 441
Student Class With Inheritance
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 442
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–2019 Michael D. Adams C++ Version: 2019-02-04 443
Class Hierarchies
B C
D E
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 444
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–2019 Michael D. Adams C++ Version: 2019-02-04 446
Types of Inheritance
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 447
Types of Inheritance (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 448
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–2019 Michael D. Adams C++ Version: 2019-02-04 449
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–2019 Michael D. Adams C++ Version: 2019-02-04 450
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–2019 Michael D. Adams C++ Version: 2019-02-04 451
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–2019 Michael D. Adams C++ Version: 2019-02-04 452
Public Inheritance
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 453
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–2019 Michael D. Adams C++ Version: 2019-02-04 454
Protected and Private Inheritance
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 455
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–2019 Michael D. Adams C++ Version: 2019-02-04 456
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–2019 Michael D. Adams C++ Version: 2019-02-04 457
Inheritance and Constructors
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 458
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–2019 Michael D. Adams C++ Version: 2019-02-04 459
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–2019 Michael D. Adams C++ Version: 2019-02-04 460
Inheritance, Assignment Operators, and Destructors
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 461
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–2019 Michael D. Adams C++ Version: 2019-02-04 462
Construction and Destruction Order
■ order of construction:
1 if most-derived class in hierarchy, initialize all virtual base class objects in
hierarchy in order of depth-first left-to-right traversal of graph of base class
declarations, where left to right refers to order of appearance of base class
names in class definition (virtual base classes to be discussed later)
2 initialize non-virtual (direct) base class objects in order listed in class
definition
3 initialize non-static data members in order of declaration in class definition
4 execute constructor body
■ order of destruction is exact reverse of order of construction, namely:
1 execute destructor body
order
4 if most-derived class in hierarchy, destroy all virtual base class objects in
hierarchy in reverse of construction order
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 463
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–2019 Michael D. Adams C++ Version: 2019-02-04 464
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–2019 Michael D. Adams C++ Version: 2019-02-04 465
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–2019 Michael D. Adams C++ Version: 2019-02-04 466
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–2019 Michael D. Adams C++ Version: 2019-02-04 467
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–2019 Michael D. Adams C++ Version: 2019-02-04 468
Upcasting Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 469
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–2019 Michael D. Adams C++ Version: 2019-02-04 471
Inheritance and Overloading
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 472
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–2019 Michael D. Adams C++ Version: 2019-02-04 473
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–2019 Michael D. Adams C++ Version: 2019-02-04 474
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–2019 Michael D. Adams C++ Version: 2019-02-04 475
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–2019 Michael D. Adams C++ Version: 2019-02-04 476
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–2019 Michael D. Adams C++ Version: 2019-02-04 477
Section 2.7.2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 478
Run-Time Polymorphism
■ polymorphism is characteristic of being able to assign different meaning
to something in different contexts
■ polymorphism that occurs at run time called run-time polymorphism
(also known as dynamic polymorphism)
■ in context of inheritance, key type of run-time polymorphism is
polymorphic function call (also known as dynamic dispatch)
■ when inheritance relationship exists between two classes, type of
reference or pointer to object may not correspond to actual dynamic (i.e.,
run-time) type of object referenced by reference or pointer
■ that is, reference or pointer to type T may, in fact, refer to object of type D,
where D is either directly or indirectly derived from T
■ when calling member function through pointer or reference, may want
actual function invoked to be determined by dynamic type of object
referenced by pointer or reference
■ function call with this property said to be polymorphic
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 479
Virtual Functions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 480
Virtual Functions (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 481
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–2019 Michael D. Adams C++ Version: 2019-02-04 482
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–2019 Michael D. Adams C++ Version: 2019-02-04 483
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–2019 Michael D. Adams C++ Version: 2019-02-04 484
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–2019 Michael D. Adams C++ Version: 2019-02-04 485
Constructors, Destructors, and Virtual Functions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 486
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–2019 Michael D. Adams C++ Version: 2019-02-04 487
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–2019 Michael D. Adams C++ Version: 2019-02-04 488
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–2019 Michael D. Adams C++ Version: 2019-02-04 489
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–2019 Michael D. Adams C++ Version: 2019-02-04 491
Pure Virtual Functions
■ pure virtual function can still be defined, although likely only useful in case
of virtual destructor
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 492
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–2019 Michael D. Adams C++ Version: 2019-02-04 493
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–2019 Michael D. Adams C++ Version: 2019-02-04 494
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–2019 Michael D. Adams C++ Version: 2019-02-04 495
The dynamic_cast Operator
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 498
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–2019 Michael D. Adams C++ Version: 2019-02-04 499
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–2019 Michael D. Adams C++ Version: 2019-02-04 500
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–2019 Michael D. Adams C++ Version: 2019-02-04 501
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–2019 Michael D. Adams C++ Version: 2019-02-04 502
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–2019 Michael D. Adams C++ Version: 2019-02-04 503
Section 2.7.3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 504
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–2019 Michael D. Adams C++ Version: 2019-02-04 505
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–2019 Michael D. Adams C++ Version: 2019-02-04 506
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–2019 Michael D. Adams C++ Version: 2019-02-04 507
Dreaded Diamond Inheritance Pattern
B C
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 508
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–2019 Michael D. Adams C++ Version: 2019-02-04 509
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 in class definition
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 510
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–2019 Michael D. Adams C++ Version: 2019-02-04 511
Section 2.7.4
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 512
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 513
Section 2.8
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 514
C++ Standard Library
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 515
C++ Standard Library (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 516
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–2019 Michael D. Adams C++ Version: 2019-02-04 517
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–2019 Michael D. Adams C++ Version: 2019-02-04 518
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–2019 Michael D. Adams C++ Version: 2019-02-04 520
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–2019 Michael D. Adams C++ Version: 2019-02-04 521
Commonly-Used Header Files (Continued 5)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 522
Section 2.8.1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 523
Standard Template Library (STL)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 524
Containers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 525
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–2019 Michael D. Adams C++ Version: 2019-02-04 526
Associative Containers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 527
Typical Sequence Container Member Functions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 528
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–2019 Michael D. Adams C++ Version: 2019-02-04 529
Motivation for Iterators
begin end
■ organization of elements in doubly-linked list container:
begin end
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 530
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–2019 Michael D. Adams C++ Version: 2019-02-04 531
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–2019 Michael D. Adams C++ Version: 2019-02-04 532
Abilities of Iterator Categories
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 533
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–2019 Michael D. Adams C++ Version: 2019-02-04 534
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–2019 Michael D. Adams C++ Version: 2019-02-04 535
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–2019 Michael D. Adams C++ Version: 2019-02-04 536
Bidirectional Iterators
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 537
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–2019 Michael D. Adams C++ Version: 2019-02-04 538
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–2019 Michael D. Adams C++ Version: 2019-02-04 539
Iterator Gotchas
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 540
Algorithms
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 541
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–2019 Michael D. Adams C++ Version: 2019-02-04 542
Functions (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 543
Functions (Continued 2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 544
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–2019 Michael D. Adams C++ Version: 2019-02-04 545
Functions (Continued 4)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 546
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–2019 Michael D. Adams C++ Version: 2019-02-04 547
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–2019 Michael D. Adams C++ Version: 2019-02-04 548
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–2019 Michael D. Adams C++ Version: 2019-02-04 549
Functions (Continued 8)
Functions
. . . . . . . . . . .for
. . . Uninitialized
. . . . . . . . . . . . . .Storage
........
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 550
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–2019 Michael D. Adams C++ Version: 2019-02-04 551
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–2019 Michael D. Adams C++ Version: 2019-02-04 552
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–2019 Michael D. Adams C++ Version: 2019-02-04 553
Section 2.8.2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 554
The std::array Class Template
2 http://www.cplusplus.com/reference/stl/array
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 555
Member Types
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 556
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–2019 Michael D. Adams C++ Version: 2019-02-04 557
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–2019 Michael D. Adams C++ Version: 2019-02-04 558
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–2019 Michael D. Adams C++ Version: 2019-02-04 559
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–2019 Michael D. Adams C++ Version: 2019-02-04 560
Section 2.8.3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 561
The std::vector Class Template
2 http://en.cppreference.com/w/cpp/container/vector
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 562
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–2019 Michael D. Adams C++ Version: 2019-02-04 563
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–2019 Michael D. Adams C++ Version: 2019-02-04 564
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–2019 Michael D. Adams C++ Version: 2019-02-04 565
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–2019 Michael D. Adams C++ Version: 2019-02-04 566
Invalidation of References, Iterators, and Pointers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 567
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); elements in old array
moved/copied to new array; 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–2019 Michael D. Adams C++ Version: 2019-02-04 568
vector Example: Constructors
1 std::vector<double> v0;
2 // empty vector
3
4 std::vector<double> v1(10);
5 // vector with 10 elements, each initialized to 0.0
6 // (effectively via value initialization)
7
8 std::vector<double> v2(10, 5.0);
9 // vector with 10 elements, each initialized to 5.0
10
11 std::vector<int> v3{1, 2, 3};
12 // vector with 3 elements: 1, 2, 3
13 // std::initializer_list (note brace brackets)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 569
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–2019 Michael D. Adams C++ Version: 2019-02-04 570
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–2019 Michael D. Adams C++ Version: 2019-02-04 571
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–2019 Michael D. Adams C++ Version: 2019-02-04 572
Section 2.8.4
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 573
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:
2 http://www.cplusplus.com/reference/string/basic_string
2 http://en.cppreference.com/w/cpp/string/basic_string
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 574
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–2019 Michael D. Adams C++ Version: 2019-02-04 575
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–2019 Michael D. Adams C++ Version: 2019-02-04 576
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–2019 Michael D. Adams C++ Version: 2019-02-04 577
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–2019 Michael D. Adams C++ Version: 2019-02-04 578
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–2019 Michael D. Adams C++ Version: 2019-02-04 579
Member Functions (Continued 4)
Allocator
Member Name Description
get_allocator get allocator
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 580
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–2019 Michael D. Adams C++ Version: 2019-02-04 581
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–2019 Michael D. Adams C++ Version: 2019-02-04 582
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–2019 Michael D. Adams C++ Version: 2019-02-04 583
Section 2.8.5
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 584
The std::pair Class Template
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 585
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(true, 42);
10 assert(p == q);
11 p = {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–2019 Michael D. Adams C++ Version: 2019-02-04 586
The std::tuple Class Template
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 587
tuple Example
1 #include <tuple>
2 #include <cassert>
3
4 int main() {
5 std::tuple t(true, 42, ’Z’);
6 auto u = std::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::tuple(true, 1, ’1’);
14 v.swap(u);
15 assert(std::get<0>(v));
16 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 588
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;
■ T is type of optional value
■ T cannot be reference type
■ at any given point in time, object either contains value or does not
■ object can be given value by initialization or assignment
■ common use case is return value of function that can fail
■ std::bad_optional_access exception indicates checked access to
optional object that does not contain value
■ optional value is required to be stored directly in optional object itself
[C++17 23.6.3/1]
⁓⁓⁓⁓⁓⁓⁓⁓
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 589
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–2019 Michael D. Adams C++ Version: 2019-02-04 590
optional Member Functions (Continued)
Modifiers
Name Description
swap exchange contents
reset clear any contained value
emplace constructs contained value in place
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 591
optional Example
1 #include <optional>
2 #include <string>
3 #include <exception>
4 #include <cassert>
5 #include <iostream>
6
7 int main() {
8 using namespace std::literals;
9 auto s = std::optional("Hello!"s);
10 assert(s && s.has_value());
11 assert(s.value() == "Hello!");
12 auto t = std::optional("Goodbye!"s);
13 s.swap(t);
14 assert(*s == "Goodbye!" && *t == "Hello!");
15 s.reset();
16 assert(!s && !s.has_value());
17 std::cout << s.value_or("Goodbye!") << ’\n’;
18 try {std::cout << s.value() << ’\n’;}
19 catch (const std::bad_optional_access&) {
20 std::cout << "caught exception\n";
21 }
22 s.emplace("Salut!");
23 std::cout << s.value() << ’\n’;
24 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 592
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–2019 Michael D. Adams C++ Version: 2019-02-04 593
The std::variant Class Template
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 594
variant Member Functions
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–2019 Michael D. Adams C++ Version: 2019-02-04 595
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–2019 Michael D. Adams C++ Version: 2019-02-04 596
The std::any Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 597
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–2019 Michael D. Adams C++ Version: 2019-02-04 598
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–2019 Michael D. Adams C++ Version: 2019-02-04 599
Section 2.8.6
Time Measurement
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 600
Time Measurement
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 601
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–2019 Michael D. Adams C++ Version: 2019-02-04 602
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–2019 Michael D. Adams C++ Version: 2019-02-04 603
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–2019 Michael D. Adams C++ Version: 2019-02-04 604
Section 2.8.7
Miscellany
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 605
The std::basic_string_view Class Template
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 606
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–2019 Michael D. Adams C++ Version: 2019-02-04 607
Section 2.9
Miscellany
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 608
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.
2 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.
2 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.
2 Look for A in the namespace in which the type of X was declared, in the
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 609
Argument-Dependent Lookup (ADL)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 610
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–2019 Michael D. Adams C++ Version: 2019-02-04 611
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–2019 Michael D. Adams C++ Version: 2019-02-04 612
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–2019 Michael D. Adams C++ Version: 2019-02-04 613
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–2019 Michael D. Adams C++ Version: 2019-02-04 614
Part 3
More C++
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 615
Section 3.1
Initialization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 616
Typical Memory Organization for Program
Heap
Stack
Environment and
High Address Program Arguments
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 618
Storage Duration
■ storage duration: how long memory for object exists
■ four types of storage duration:
1 automatic
2 static
3 dynamic
4 thread
■ automatic storage duration: storage allocated at start of enclosing code
block and deallocated at end
■ all local objects have automatic storage duration, except those declared
with static, extern, or thread_local qualifiers
■ objects with automatic storage duration stored on stack
■ static storage duration: storage is allocated at start of program and
deallocated when program ends
■ all objects declared at namespace scope (including global namespace)
have static storage duration as well as those declared with static or
extern qualifiers (e.g., static data members and static function-local
variables)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 619
Storage Duration (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 620
Initialization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 621
Types of Initialization
Character Array
List Aggregate
Initialization
Initialization Initialization
From String Literal
Zero Default
Initialization Initialization
■ constant initialization can use all other types of initialization (i.e., zero, default,
value, direct, copy, list, and aggregate)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 623
Initialization Phases
■ for static initialization, only constant and zero initialization used directly
(but constant initialization can result in other types of initialization being
invoked indirectly)
■ for dynamic initialization, all types of initialization other than constant
initialization can be used
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 624
How May I Initialize Thee? Let Me Count the Ways
1 #include <complex>
2 #include <string>
3 #include <array>
4 using Complex = std::complex<float>;
5
6 Complex gz{0, 1}; // gz is constant initialized (statically)
7 int gi; // gi is zero initialized (statically)
8 char buf[1024]; // buf is zero initialized (statically)
9
10 int main() {
11 int i; // i is default initialized (to indeterminate value)
12 Complex z1; // z1 is default initialized
13 Complex z2(); // function declaration
14 Complex z3{}; /* z3 is value initialized as part of
15 direct-list initialization */
16 Complex z4{1, -1}; /* z4 is direct initialized as part of
17 direct-list initialization */
18 Complex z5(1, -1); // z5 is direct initialized
19 Complex z6 = {1, -1}; /* z6 is copy initialized as part of
20 copy-list initialization */
21 Complex z7 = Complex(1, -1); // z7 is copy initialized
22 static Complex u = Complex(); // u is constant initialized (statically)
23 z1 = {1, -1}; /* temporary object is direct initialized as part of
24 direct-list initialization */
25 z1 = {}; /* temporary object is value initialized as part of
26 direct-list initialization */
27 std::array<int, 3> a{1, 2, 3}; /* a is aggregate initialized as part
28 of direct-list initialization */
29 std::string s1{’h’, ’i’}; // s1 is direct-list initialized
30 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 625
Initialization Order and Namespace-Scope Static-Storage-Duration Objects
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 627
Example: Initialization Order Fiasco
util_1.cpp
1 #include <vector>
2
3 std::vector<int> v{1, 2, 3, 4};
4 // invoked constructor is not constexpr; cannot use
5 // constant initialization; constructor invoked as
6 // part of dynamic initialization
util_2.cpp
1 #include <vector>
2 extern std::vector<int> v;
3
4 std::vector<int> w{v[0], v[1]};
5 // arguments for invoked constructor not constant
6 // expressions; cannot use constant initialization;
7 // constructor invoked as part of dynamic initialization;
8 // construction of w can invoke undefined behavior
9 // since v might not yet have been constructed
main.cpp
1 int main() {
2 // ...
3 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 628
Constant Initialization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 629
Constant Initialization Example: Smart Pointers
1 #include <memory>
2 #include <string>
3
4 std::unique_ptr<std::string> p1; /* std::unique_ptr<std::string>
5 not of literal type, but...
6 constructor being invoked is constexpr; p1 is constant
7 initialized */
8
9 std::unique_ptr<std::string> p2(nullptr); /*
10 std::unique_ptr<std::string> not of literal type, but...
11 constructor being invoked is constexpr and argument to
12 constructor is constant expression; p2 is constant
13 initialized */
14
15 std::unique_ptr<std::string> q1; /* std::shared_ptr<std::string>
16 not of literal type, but...
17 constructor being invoked is constexpr; q1 is constant
18 initialized */
19
20 std::shared_ptr<std::string> q2(nullptr); /*
21 std::shared_ptr<std::string> not of literal type, but...
22 constructor being invoked is constexpr and argument to
23 constructor is constant expression; q2 is constant
24 initialized */
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 630
Constant Initialization Example: Fiasco-Free Initialization
constant_initialization_2_util.hpp
1 #include <complex>
2 extern std::complex<double> z0;
3 extern std::complex<double> z1;
util_1.cpp
1 #include "constant_initialization_2_util.hpp"
2 constexpr double x{1};
3 constexpr double y{2};
4 std::complex<double> z0{x, y};
5 // invoked constructor is constexpr; all arguments to
6 // constructor are constant expressions; as part of static
7 // initialization, z0 is constant initialized to (1, 2)
util_2.cpp
1 #include "constant_initialization_2_util.hpp"
2 std::complex<double> z1 = z0 - std::complex<double>{0, 2};
3 // as part of dynamic initialization, z1 is copy
4 // initialized to (1, 2) - (0, 2) = (1, 0); static
5 // initialization of z0 guaranteed to have been already
6 // performed
main.cpp
1 int main() {/* ... */}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 631
Zero Initialization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 632
Zero Initialization (Continued)
literal 0 to T
2 if T is class type, each of following is zero initialized and any padding
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 633
Zero Initialization Example
1 #include <string>
2
3 struct Point {int x; int y;};
4
5 static int ga[2]; /* ga is statically zero initialized
6 (to {0, 0}) 1 */
7 static int gb[2] = {1}; /* gb[1] is statically zero initialized
8 (to 0) as part of constant initializing gb to {1, 0} 1 */
9 char *gp; /* gp is statically zero initialized (to null
10 pointer) 1 */
11 std::string gs; /* gs is statically zero initialized
12 (to indeterminate value) and (later) dynamically
13 default initialized to empty string 1 */
14 int gi; // gi is statically zero initialized (to 0)
15
16 int main() {
17 char buf[4] = "hi"; /* buf[3] is zero initialized (to 0) as
18 part of initializing buf to {’h’, ’i’, ’\0’, 0} 2 */
19 static float f; /* f is statically zero initialized
20 (to 0.0f) 1 */
21 int i{}; /* i is zero initialized (to 0) as part of
22 list initialization 3 */
23 const Point& p = Point(); /* referenced object is
24 zero initialized (to {0, 0}) as part of
25 value initialization 3 */
26 }
Note: n indicates case n from earlier slide
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 634
Default Initialization
against empty argument list and called constructor provides initial value for
new object
2 if T is array type, each element of array is default initialized
2 otherwise, nothing is done (which results in indeterminate value in case of
object with automatic storage duration)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 635
Default Initialization Example
1 #include <string>
2
3 struct Widget {
4 Widget() {} /* w is default initialized to indeterminate
5 value 3 */
6 int w;
7 };
8
9 static std::string gs; /* gs is (statically) zero initialized
10 and then (dynamically) default initialized to
11 empty string 1 */
12
13 int main() {
14 std::string s; /* s is default initialized to
15 empty string 1 */
16 std::string* sp = new std::string; /* heap-allocated object is
17 default initialized to empty string 2 */
18 int i; /* i is default initialized to indeterminate
19 value 1 */
20 int* ip = new int; /* heap-allocated object is
21 default initialized to indeterminate value 2 */
22 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 636
Value Initialization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 637
Value Initialization (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 638
Value Initialization Example
1 #include <string>
2
3 struct Point {int x; int y;};
4 struct Widget {
5 Widget() : x() {} // x is value initialized to 0 3
6 int x; int y{}; // y is value initialized to 0 4
7 };
8
9 int main() {
10 Point p; // p is default initialized to indeterminate value
11 Point q{}; /* q is aggregate initialized to {0, 0} as part of
12 list initialization */
13 Point* p1 = new Point(); /* heap-allocated object is
14 value initialized to {0, 0} 2 */
15 Point* p2 = new Point{}; /* heap-allocated object is aggregate
16 initialized to {0, 0} as part of list initialization */
17 std::string s{}; /* s is value initialized to empty string as part
18 of list initialization 4 */
19 const Point& pr = Point(); /* referenced object is value initialized
20 to {0, 0} 1 */
21 const Point& pr2 = Point{}; /* referenced object is aggregate
22 initialized to {0, 0} as part of list initialization */
23 const std::string& sr = std::string{}; /* referenced object is
24 value initialized to empty string as part of
25 list initialization 1 */
26 Widget w{}; /* w is value initialized to {0, 0} as part of
27 list initialization 4 */
28 int i{}; /* i is value initialized to 0 as part of
29 list initialization 4 */
30 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 639
Direct Initialization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 640
Direct Initialization (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 641
Direct Initialization Example
1 #include <string>
2 #include <vector>
3
4 struct Widget {
5 Widget() : s("hi") {} // s is direct initialized to "hi" 5
6 explicit Widget(const std::string& s_) : s(s_) {}
7 std::string s;
8 std::string t{"bye"}; /* t is direct initialized to "bye" via
9 direct-list initialization 1 */
10 };
11
12 int main() {
13 std::vector<int> u(2, 42); // u is direct initialized to {42, 42} 1
14 std::vector<int> v(3); // v is direct initialized to {0, 0, 0} 1
15 std::string s("bye"); // s is direct initialized to "bye" 1
16 int i(1); // i is direct initialized to 1 1
17 int j{1}; /* j is direct initialized to 1 as part of
18 direct-list initialization */
19 double d = static_cast<double>(i); /* temporary object is
20 direct initialized to 1.0 3 */
21 std::string* sp = new std::string("hi"); /* heap-allocated object is
22 direct initialized to "hi" 2 */
23 [s](){return s.size();}(); /* s data member in closure is
24 direct initialized to value of s in main 5 */
25 Widget w = Widget("hi"); /* temporary object is direct initialized
26 to {"hi", "bye"} 4 */
27 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 642
Copy Initialization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 643
Copy Initialization (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 644
Copy Initialization Example
1 #include <string>
2 using namespace std::literals;
3
4 struct Widget {
5 std::string s = "hi"; // s is copy initialized to "hi" 1
6 };
7
8 std::string identity(std::string p) {
9 return p; // return value copy initialized from p 3
10 }
11
12 int main() {
13 std::string a[2] = {"hi", "bye"}; /*
14 as part of aggregate initialization:
15 a[0] is copy initialized to "hi" and
16 a[1] is copy initialized to "bye" 6 */
17 std::string s = "hello"s; // s is copy initialized to "hello" 1
18 std::string t = {3, ’A’}; /* t is copy initialized to "AAA" as
19 part of copy-list initialization 1 */
20 s = identity(s); // function parameter is copy initialized from s 2
21 try {
22 throw t; // exception object copy initialized from t 4
23 } catch (std::string s) {
24 // s is copy initialized from exception object 5
25 }
26 if (auto i = s.begin(); i != s.end()) {/* ... */}
27 // i is copy initialized from s.begin() 1
28 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 645
Aggregates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 646
Aggregate Initialization
■ aggregate initialization: initializes aggregate from braced initializer list
(or string literal)
■ aggregate initialization is used:
2 when aggregate is initialized by braced initializer list (or string literal in case
of initializing character array, since braces around string literal are implied)
[C++17 11.6.1/3]
⁓⁓⁓⁓⁓⁓⁓⁓
■ approximately speaking, aggregate initialization has following behavior:
2 each direct public base, array element, or non-static class member in order
of array subscript or appearance in class definition copy initialized from
corresponding clause of initializer list
2 if initializer clause is expression, only non-narrowing implicit conversions
allowed
2 if initializer clause is braced initializer list, base/element/member list initialized
from clause
2 if number of initializer clauses less than number of members and bases,
remaining members and bases initialized by default initializer if provided in
class definition and otherwise by empty lists in accordance with usual list
initialization rules (e.g., performs value initialization for non-class types and
non-aggregate classes with default constructors)
2 if T is union type, only first non-static data member initialized
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 647
Aggregate Initialization Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 648
List Initialization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 650
List Initialization (Continued 2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 651
List Initialization (Continued 3)
■ approximately speaking, list initialization of object of non-aggregate type T
has following behavior: [C++17 11.6.4/3]
⁓⁓⁓⁓⁓⁓⁓⁓
2 if T is class type with default constructor and braced initializer list empty,
1 #include <initializer_list>
2 #include <vector>
3
4 struct Widget {
5 Widget() : w{1, 2, 3} {} // w is direct-list initialized 5
6 Widget(std::initializer_list<int> w_) : w{w_} {} /* w is
7 direct initialized as part of direct-list initialization 5 */
8 std::vector<int> v{3, 2, 1}; // v is direct-list initialized 4
9 std::vector<int> w;
10 };
11
12 int main() {
13 Widget w1{1, 2, 3}; // w1 is direct-list initialized 1
14 const Widget& w2 = Widget{1, 2, 3};
15 // temporary object is direct-list initialized 2
16 Widget* w3 = new Widget{1, 2, 3};
17 // heap-allocated object is direct-list initialized 3
18 for (auto&& i : {1, 2, 3}) {}
19 // temporary object is direct-list initialized 2
20 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 653
Copy-List Initialization Example
1 #include <vector>
2 #include <tuple>
3 #include <initializer_list>
4
5 struct Widget {
6 Widget() : v({1, 2, 3}) {}
7 // constructor argument is copy-list initialized 2
8 Widget(std::initializer_list<int> v_) : v{v_} {}
9 const int& operator[] (std::pair<int, int> i) const
10 {return i.first ? v[i.second] : w[i.second];}
11 std::vector<int> v;
12 std::vector<int> w = {3, 2, 1}; // w is copy-list initialized 7
13 };
14
15 Widget func(Widget w) {
16 return {1, 2, 3}; // returned value is copy-list initialized 3
17 }
18
19 int main() {
20 Widget w = {1, 2, 3}; // w is copy-list initialized 1
21 w = {1, 2, 3}; // temporary object is copy-list initialized 5
22 func({1, 2, 3}); // function argument is copy-list initialized 2
23 Widget({1, 2, 3}); // constructor argument is copy-list initialized 6
24 int i = w[{0, 1}];
25 // operator[] function parameter is copy-list initialized 4
26 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 654
Initialization of Character Array From String Literal
■ special rule employed for initializing character array from string literal
[C++17 11.6/17.3]
⁓⁓⁓⁓⁓⁓⁓⁓⁓
■ array element type and character type of string literal must be compatible
■ each character in string literal (including null-terminator) placed in order
into successive array elements ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 11.6.2]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 655
Character Array Initialization Example
1 int main() {
2 char a1[] = "hi";
3 // initialized to {’h’, ’i’, ’\0’} from string literal
4 char a2[] = {"hi"}; /* initialized to {’h’, ’i’, ’\0’} as
5 part of copy-list initialization */
6 char a3[]{"hi"};
7 char a4[3]{"hi"};
8 /* each of a3 and a4 is initialized to {’h’, ’i’, ’\0’}
9 as part of direct-list initialization */
10
11 char b1[4] = "hi";
12 char b2[4] = {"hi"};
13 char b3[4]{"hi"};
14 /* each of b1, b2, and b3 is initialized to
15 {’h’, ’i’, ’\0’, ’\0’} */
16
17 char16_t c[] = u"hi"; /* initialized to
18 {u’h’, u’i’, u’\0’} from string literal */
19 char32_t d[] = U"hi"; /* initialized to
20 {U’h’, U’i’, U’\0’} from string literal */
21 wchar_t e[4]{L"hi"}; /* initialized to
22 {L’h’, L’i’, L’\0’, L’\0’} as part of
23 direct-list initialization */
24 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 656
Example: Aggregates and Brace Elision
1 struct Gadget {
2 int x;
3 int y;
4 };
5
6 struct Widget {
7 Gadget g;
8 int i;
9 };
10
11 int main() {
12 int x[2][2] = {1, 2, 3, 4};
13 // effectively initializer is {{1, 2}, {3, 4}}
14 Widget v = {1, 2, 3};
15 // effectively initializer is {{1, 2}, 3}
16 Widget w = {1, 2};
17 // effectively initializer is {{1, 2}}
18 // w initialized to {{1, 2}, 0}
19 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 657
Example: Auto and Initialization
1 #include <initializer_list>
2
3 auto i1 = 42; // type of i1 deduced as int
4 auto i2(42); // type of i2 deduced as int
5 auto i3{42}; // type of i3 deduced as int
6 // auto i4{42, 42}; // ERROR: exactly one element required
7 auto i5 = {42};
8 // type of i5 deduced as std::initializer_list<int>
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 658
Example: Braced Initializer Lists and Constructor Selection
1 #include <initializer_list>
2
3 struct Widget {
4 Widget();
5 Widget(std::initializer_list<int>);
6 Widget(int);
7 };
8
9 int main() {
10 Widget w{};
11 // invokes default constructor; for empty
12 // braced initializer list, default constructor
13 // preferred over std::initializer_list constructor
14 Widget v{42};
15 // invokes constructor taking std::initializer_list;
16 // for non-empty braced initializer list, constructor
17 // taking std::initializer_list preferred over those
18 // that do not
19 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 659
Example: List Initialization
1 #include <map>
2 #include <vector>
3 #include <string>
4
5 std::map<int, std::string> m{
6 {42, "forty two"},
7 {0, "zero"}
8 }; // initialized to map with two elements
9
10 std::vector<std::string> v1{"hi", "bye"};
11 // initialized to vector with two elements
12
13 std::vector<std::string> v2{{"hi", "bye"}};
14 // ERROR: will try to initialize to vector with
15 // one element; invokes std::string constructor that
16 // takes two iterators as parameters; pointers
17 // to "hi" and "bye" passed as begin and end
18 // iterators; this results in undefined behavior
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 660
Example: Initialization and Narrowing Conversions
1 struct Widget {
2 Widget(int i_) : i(i_) {}
3 int i;
4 };
5
6 int main() {
7 Widget v(42.0);
8 // OK: narrowing conversion allowed in
9 // direct initialization
10 // Widget w{42.0};
11 // ERROR: narrowing conversion not allowed in
12 // list initialization
13 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 661
Example: Initializers and Braces/Parentheses
1 #include <vector>
2 #include <string>
3
4 std::vector<int> v1(3, 42);
5 // initialized to vector with elements 42, 42, 42
6 std::vector<int> v2{3, 42};
7 // initialized to vector with elements 3, 42
8
9 std::string s1(3, ’a’);
10 // initialized to string consisting of 3 ’a’ characters
11 std::string s2{3, ’a’};
12 // initialized to string consisting of characters ’\3’, ’a’
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 662
Example: std::initializer_list and Lifetime
1 #include <iostream>
2 #include <initializer_list>
3
4 auto f(int a, int b, int c) {
5 return std::initializer_list<int>{a, b, c};
6 // ERROR: initializer_list references elements in
7 // temporary array whose lifetime need not extend
8 // beyond lifetime of initializer_list;
9 // therefore, returned initializer_list
10 // likely references invalid data
11 }
12
13 int main() {
14 // nothing good likely to happen here
15 for (auto i : f(1, 2, 3)) {
16 std::cout << i << ’\n’;
17 }
18 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 663
Section 3.1.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 664
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 665
Section 3.2
Temporary Objects
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 666
Temporary Objects
■ A temporary object is an unnamed object introduced by the compiler.
■ Temporary objects may be used during: ⁓⁓⁓⁓⁓⁓⁓
[C++17 15.2/1] [C++17 15.2/2]
⁓⁓⁓⁓⁓⁓⁓
2 evaluation of expressions
2 argument passing
2 function returns (that return by value)
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 667
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–2019 Michael D. Adams C++ Version: 2019-02-04 668
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–2019 Michael D. Adams C++ Version: 2019-02-04 669
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–2019 Michael D. Adams C++ Version: 2019-02-04 670
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;
22 // no temporaries, int increment, operator=
23 y = x++;
24 // 1 temporary, 1 named, 2 constructors,
25 // 2 destructors, int increment, operator=
26 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 671
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–2019 Michael D. Adams C++ Version: 2019-02-04 672
Lifetime of Temporary Objects
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 673
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–2019 Michael D. Adams C++ Version: 2019-02-04 674
Temporary Object Creation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 675
Section 3.3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 676
Expressions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 677
Value Categories of Expressions
expression
glvalue
rvalue
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 678
Value Categories of Expressions (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 679
Lvalues
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 680
Lvalues (Continued 1)
int& get_value();
++get_value(); // get_value() is an lvalue
■ A string literal is an lvalue. [C++17 8.1.1/1]
⁓⁓⁓⁓⁓⁓⁓
Example:
const char *s = "Hello"; // "Hello" is an lvalue
■ A named rvalue reference is an lvalue. [C++17 8/7]
⁓⁓⁓⁓⁓
Example:
int&& i = 1 + 3;
int j = i; // i is an lvalue
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 681
Lvalues (Continued 2)
■ An rvalue reference to a function (both named and unnamed) is an
lvalue. ⁓⁓⁓⁓⁓⁓
[C++17 8/7] Example:
void func();
void (&&f)() = func;
f(); // f is an lvalue
std::move(func)(); // std::move(func) is an lvalue
■ The result of each of the following built-in operators is an lvalue:
2 built-in subscripting operator (except when array rvalue involved) [C++17 ⁓⁓⁓⁓⁓⁓⁓
8.2.1/1]
2 built-in indirection operator [C++17 8.3.1/1]
⁓⁓⁓⁓⁓⁓⁓
2 built-in pre-increment and pre-decrement operators ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 8.3.2/1] [C++17 8.3.2/2]
⁓⁓⁓⁓⁓⁓⁓⁓
2 built-in assignment and compound-assignment operators [C++17 ⁓⁓⁓⁓⁓⁓⁓
8.18/1]
Example:
char buffer[] = "Hello";
char* s = buffer;
*s = ’a’; // *s is an lvalue
*(s + 1) = ’b’; // *(s + 1) is an lvalue
++s; // ++s is an lvalue
--s; // --s is an lvalue
s += 2; // s += 2 is an lvalue
s = &buffer[1];
// s = &buffer[1] is an lvalue
// buffer[1] is an lvalue
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 682
Moving and Lvalues
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 683
Rvalues
■ A prvalue (i.e., pure rvalue) is an expression whose evaluation: [C++17 6.10/1]
⁓⁓⁓⁓⁓⁓⁓
2 computes the value of an operand of an operator; or
2 initializes an object or a bitfield.
■ A prvalue never corresponds to an object (but, in some contexts, might be
used to materialize a temporary object).
■ A prvalue does not have an identity.
■ An xvalue (i.e., expiring value) is an expression that: [C++17 6.10/1]
⁓⁓⁓⁓⁓⁓⁓⁓
2 denotes an object or bitfield (usually near the end of its lifetime);
2 has an identity; and
2 the resources of the object/bitfield can safely be reused (i.e., is deemed to
be safe to use as the source for a move).
■ An xvalue is associated with certain kinds of expressions involving rvalue
references or the materialization of a temporary object.
■ An rvalue is an expression that is either a prvalue or an xvalue.
■ Unlike an lvalue, an rvalue need not have an identity.
■ Therefore, applying the (built-in) address-of operator to an rvalue
(corresponding to an object) is not allowed. ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 8.3.1/3]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 684
Prvalues
■ The result of calling a function that returns by value (i.e., the return type is
not a reference type) is a prvalue. [C++17 8.2.2/11] Example:
⁓⁓⁓⁓⁓⁓⁓⁓
int get_value();
int i = get_value();
// get_value() is a prvalue
// Note: get_value() is not the same as get_value
■ All literals other than string literals are prvalues. [C++17 8.1.1/1]
⁓⁓⁓⁓⁓⁓⁓⁓
Example:
double pi = 3.1415; // 3.1415 is a prvalue
int i = 42; // 42 is a prvalue
i = 2 * i + 1; // 2 and 1 are prvalues
char c = ’A’; // ’A’ is a prvalue
■ The this keyword is a prvalue. [C++17 8.1.2/2]
⁓⁓⁓⁓⁓⁓⁓
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 685
Prvalues (Continued)
Example:
int i;
int j;
i = -(3 + 5); // 3 + 5 and -(3 + 5) are prvalues
j = i * i; // i * i is a prvalue
j = (i == 42); // i == 42 is a prvalue
j = (i & 7) | 2; // (i & 7) and (i & 7) | 2 are prvalues
i = j++; // j++ is a prvalue
int *ip = &i; // &i is a prvalue
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 686
Xvalues
std::string s("Hello");
std::string t = std::move(s); // std::move(s) is xvalue
// Note: std::move returns rvalue reference type
■ An unnamed rvalue reference to an object is an xvalue. [C++17 8/7]
⁓⁓⁓⁓⁓⁓
Example:
std::string s("Hello");
std::string t;
t = static_cast<std::string&&>(s);
// static_cast<std::string&&>(s) is xvalue
s = std::move(t); // std::move(t) is an xvalue
■ A temporary object materialized from a prvalue is an xvalue. ⁓⁓⁓⁓⁓⁓⁓
[C++17 7.4/1]
Example:
std::vector<int> v;
v = std::vector<int>(10, 2);
// temporary object materialized from prvalue
// std::vector<int>(10, 2) is an xvalue
std::complex<double> u;
u = std::complex<double>(1, 2);
// temporary object materialized from prvalue
// std::complex<double>(1, 2) is an xvalue
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 687
Moving and Rvalues
■ When the source object whose value is to be propagated is an rvalue,
using a move (instead of a copy) is always safe (either because this must
be so or the programmer has explicitly deemed this to be so).
■ Example (move from temporary object):
void func() {
std::vector<int> x;
x = std::vector<int>(42, 0);
/* safe to move from temporary object materialized
from prvalue std::vector<int>(42, 0) since any change
to its value cannot be observed by other code */
// ...
}
■ Example (forced move):
void func() {
std::string s("hello");
std::cout << s << ’\n’;
std::string t(std::move(s));
/* safe to move from s to t since std::move(s) is
xvalue; programmer has, in effect, said "trust me, it
is safe to use move here"; of course, if programmer is
wrong, bad things will happen; programmer correct
in this case, since value of s not used again */
std::cout << t << ’\n’;
}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 688
More on Lvalues and Rvalues
■ Lvalues and rvalues can be either modifiable or nonmodifiable.
Example:
int i = 0;
const int j = 2;
i = j + 3;
// i is modifiable lvalue
// j is nonmodifiable lvalue
// j + 3 is modifiable rvalue
const std::string getString();
std::string s = getString();
// getString() is nonmodifiable rvalue
■ Class rvalues can have cv-qualified types, while non-class rvalues always
have cv-unqualified types. ⁓⁓⁓⁓⁓⁓
[C++17 8/6] Example:
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 689
Moving and Lvalues/Rvalues
■ With regard to propagating the value from one object to another, we can
summarize the results from the earlier slides as follows:
2 If the source for a copy operation is an lvalue, the copy operation is not
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 690
Moving/Copying and Lvalues/Rvalues
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 691
Built-In Operators, Rvalues, and Lvalues
■ Aside from the exceptions noted below, all of the built-in operators require
operands that are prvalues. ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 8.2.1/1] [C++17 8.2.5/2]
⁓⁓⁓⁓⁓⁓⁓⁓
■ The operand of each of the following built-in operators must be an lvalue:
2 address of [C++17
⁓⁓⁓⁓⁓⁓⁓
8.3.1/3]
2 pre- and post-increment [C++17 8.2.6/1] [C++17 8.3.2/1]
⁓⁓⁓⁓⁓⁓⁓ ⁓⁓⁓⁓⁓⁓⁓
2 pre- and post-decrement ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 8.2.6/1] [C++17 8.3.2/1]
⁓⁓⁓⁓⁓⁓⁓⁓
■ The left operand of the following built-in operators must be an lvalue:
2 assignment [C++17 8.18/1]
⁓⁓⁓⁓⁓⁓⁓
2 compound assignment [C++17 8.18/1]
⁓⁓⁓⁓⁓⁓⁓
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 692
Operators, Lvalues, and Rvalues
■ 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.) ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 8.2.5/4]
■ The value category and type of the result produced by the ternary
conditional operator depends on the particular manner in which the
operator is employed. [C++17 8.16/5] [C++17 8.16/6]
⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 693
Implicit Lvalue-to-Rvalue Conversion
int i = 1;
int j = 2;
int k = i + j;
/* since built-in binary addition operator requires
prvalue operands, i and j implicitly converted from
lvalues to prvalues */
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 694
Section 3.4
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 695
Copy Elision
■ normally, compiler forbidden from applying optimizations to code that
would change its observable behavior (i.e., so called “as if” rule)
■ one important exception to as-if rule is copy elision
■ copy elision is code transformation that omits copy/move operation by
constructing object in place to which it would later be copied/moved
■ copy elision allows copy/move operations to be eliminated, thus avoiding
cost of copy/move constructors
■ copy elision may also eliminate need for some temporary objects, which
avoids cost of constructing and destroying those objects
■ copy elision either allowed or required in several contexts:
2 returning by value
2 passing by value
2 throwing by value
2 catching by value
2 initialization
■ in cases where copy elision is mandatory, copy/move constructors need
not be accessible or even provided at all, which provides more flexibility in
dealing with non-movable non-copyable types
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 696
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
[C++17 15.8.3/(1.1)]
⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓
■ copy elision required if (allowed as per above and) return expression is
prvalue (i.e., placeholder for temporary object) [C++17 11.6/(17.6.1)]
⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓
■ example:
1 struct Widget {
2 Widget();
3 Widget(const Widget&);
4 Widget(Widget&&);
5 // ...
6 };
7
8 Widget func1() {return Widget();}
9 // returns prvalue (i.e., placeholder for temporary object)
10 Widget func2() {Widget w; return w;} // returns named object
11
12 int main() {
13 Widget w(func1());
14 // required copy elision (not named object returned)
15 Widget x(func2());
16 // possible copy elision (named object returned)
17 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 697
Copy Elision and Returning by Value (Continued)
■ not only were two copy/move operations eliminated, need for any
temporary objects also eliminated
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 699
Return-By-Value Example 1: Without Copy Elision
■ again, consider following code (where type T is default constructible):
T callee() {return T();}
void caller() {T x(callee()); /* ... */}
■ consider what happens without copy elision (in violation of standard in this
case)
■ executing body of caller proceeds as follows:
1 storage for temporary object allocated in caller to hold return value of
callee
2 caller invokes callee
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 701
Return-By-Value Example 2: Summary
■ consider following code (where type T is default constructible and
copyable/movable):
1 T callee() {return T();}
2 void caller() {T x; x = callee(); /* ... */}
■ goal is to assign value corresponding to T() to x in caller
■ without copy elision, this would be achieved by:
default copy/move copy/move
temporary temporary assign
construct construct x
object object
in caller
in callee in caller
callee
2 caller invokes callee
callee
2 caller invokes callee
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 704
Example Where Copy Elision Allowed But Likely Impossible
1 class Widget {
2 public:
3 Widget(int) {/* ... */}
4 Widget(const Widget&) = default;
5 Widget(Widget&&) = default;
6 // ...
7 };
8
9 bool get_flag();
10
11 // eliding copy of return value is not possible
12 Widget func() {
13 Widget w(0);
14 // w must be constructed before it is known if
15 // w will be returned; so cannot know whether to
16 // construct w in returned value
17 Widget v(42);
18 // v must be constructed before it is known if
19 // v will be returned; so cannot know whether to
20 // construct v in returned value
21 if (get_flag()) {return w;}
22 else {return v;}
23 }
24
25 int main() {
26 Widget w(func());
27 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 705
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
[C++17 11.6/15][C++17 8.2.2/4]
⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓
■ copy elision always required if allowed (as per above) [C++17 11.6/(17.6.1)]
⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓
■ example:
1 struct Widget {
2 Widget();
3 Widget(const Widget&);
4 Widget(Widget&&);
5 // ...
6 };
7
8 void func(Widget w) {/* ... */}
9
10 int main() {
11 func(Widget()); // required copy elision
12 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 706
Pass-By-Value Example: Summary
■ by using copy elision, not only was one copy/move operation eliminated,
but temporary object also eliminated
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 707
Pass-By-Value Example: Without Copy Elision
■ again, consider following code (where type T is default constructible):
void callee(T p) {/* ... */}
void caller() {callee(T());}
■ consider what happens without copy elision (in violation of standard in this
case)
■ executing body of caller proceeds as follows:
1 temporary object created in caller with value corresponding to T() (via
default constructor)
2 storage for callee’s function parameter p allocated (on stack)
3 value of temporary object in caller propagated (via move/copy
constructor) to callee’s function parameter p
4 caller transfers control to callee
5 callee returns, resulting in its function parameter being destroyed (and
deallocated)
6 temporary object in caller destroyed (and deallocated)
■ overhead: one temporary object created (constructor and destructor
invocations); one move/copy required to propagate value from temporary
object elsewhere
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 708
Pass-By-Value Example: With Copy Elision
deallocated)
■ no overhead: no temporary objects created and therefore no need to
propagate values into or out of temporary objects
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 709
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 ⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 15.8.3/(1.2)]
■ example:
1 struct Widget {
2 Widget();
3 Widget(const Widget &);
4 Widget(Widget&&);
5 // ...
6 };
7
8 void func_1(){
9 throw Widget(); // required copy elision (prvalue)
10 }
11
12 void func_2(){
13 Widget w; throw w; // possible copy elision (not prvalue)
14 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 710
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
[C++17 15.8.3/(1.3)]
⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓
■ in this context, copy elision never required
■ example:
1 struct Widget {
2 Widget();
3 Widget(const Widget &);
4 Widget(Widget&&);
5 // ...
6 };
7
8 int main() {
9 try {throw Widget();}
10 catch (Widget foo) { // possible copy elision
11 /* ... (foo not modified) */
12 }
13 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 711
Copy Elision and Initialization
■ example:
1 class Widget {
2 public:
3 Widget() {/* ... */}
4 Widget(const Widget&) {/* ... */}
5 Widget(Widget&&) {/* ... */}
6 // ...
7 };
8
9 Widget func();
10
11 int main() {
12 Widget w = Widget(func());
13 // copy elision required for initialization;
14 // no copy/move in main function;
15 // returned value from func directly constructed in w;
16 // func may need copy/move to propagate return value
17 // out of func, if not elided
18 Widget u{Widget()};
19 // copy elision required for initialization;
20 // no copy/move; new Widget object is default
21 // constructed directly in u
22 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 712
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 Widget u(Widget());
23 // function declaration
24 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 713
Maximally Delayed Materialization of Temporary Objects
1 #include <cassert>
2
3 class Widget {
4 public:
5 Widget() : c_(0) {}
6 Widget(const Widget& other) : c_(other.c_ + 1) {}
7 Widget& operator=(const Widget& other) {c_ = other.c_ + 1; return *this;}
8 int count() const {return c_;}
9 private:
10 int c_;
11 };
12
13 Widget widget_1() {return Widget();}
14 // mandatory copy elision for return value
15 Widget widget_2() {return widget_1();}
16 // mandatory copy elision for return value
17 Widget widget_3() {return widget_2();}
18 // mandatory copy elision for return value
19
20 int main() {
21 Widget w{widget_3()}; // no temporary object; no copy/move
22 assert(w.count() == 0);
23 w = widget_3();
24 // widget_1 directly constructs return value into
25 // temporary object in main
26 assert(w.count() == 1);
27 Widget v{Widget(Widget(Widget(Widget(Widget(Widget())))))};
28 // default constructs directly into v; no copy/move
29 assert(v.count() == 0);
30 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 714
Return Statements and Moving/Copying
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 715
Example: Return Statements and Moving/Copying
1 #include <string>
2
3 class Widget {
4 public:
5 Widget(const std::string&) {/* ... */}
6 Widget(const Widget&) = default;
7 Widget(Widget&&) = default;
8 // ...
9 };
10
11 Widget get_value_1() {
12 Widget w("goodbye");
13 return w;
14 // copy elision is allowed, but not required;
15 // if move/copy not elided:
16 // since w is local object, w first treated as if rvalue,
17 // resulting in move constructor being selected
18 // to propagate return value to caller
19 }
20
21 Widget get_value_2() {
22 std::string s("hello");
23 return s;
24 // effectively: return Widget(s);
25 // copy elision required (since returning temporary)
26 }
27
28 int main() {
29 Widget w(get_value_1());
30 Widget v(get_value_2());
31 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 716
Use of std::move in Return Expressions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 717
Example: Incorrect Use of std::move
1 #include <string>
2
3 class Widget {
4 public:
5 Widget(const std::string&) {/* ... */}
6 Widget(const Widget&) = default;
7 Widget(Widget&&) = default;
8 // ...
9 };
10
11 Widget get_value_1() {
12 return Widget("hello");
13 // copy elision required
14 }
15
16 // Note: This type of usage of std::move is highly undesirable,
17 // as it prevents copy elision.
18 Widget get_value_2() {
19 return std::move(Widget("hello"));
20 // since cv-unqualified return-expression type and
21 // cv-unqualified return type differ, copy elision is not
22 // allowed; move constructor is selected to propagate return
23 // value to caller; if std::move had not been used, copy/move
24 // would have been completely eliminated (due to required
25 // copy elision)
26 }
27
28 int main() {
29 Widget w(get_value_1());
30 Widget v(get_value_2());
31 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 718
Copy/Move/Elide Example: Widget (1)
1 #include <utility>
2
3 class Widget {
4 public:
5 Widget();
6 Widget(const Widget&);
7 Widget(Widget&&);
8 Widget& operator=(const Widget&);
9 Widget& operator=(Widget&&);
10 // ...
11 };
12
13 Widget func_0(Widget w) {
14 // copy elision for return value not allowed since object being
15 // returned is function parameter; treating return expression as
16 // rvalue results in move being performed
17 return w;
18 }
19
20 Widget func_1() {
21 try {
22 // copy elision for exception object is required
23 throw Widget();
24 } catch (Widget w) {
25 // copy elision for return value not allowed since object
26 // being returned is catch-clause parameter; treating return
27 // expression as rvalue results in move being performed
28 return w;
29 }
30 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 719
Copy/Move/Elide Example: Widget (2)
32 Widget func_2(Widget& w) {
33 // copy elision for return value not allowed for several reasons
34 // (e.g., function parameter, not automatic, cv-unqualified type
35 // mismatch); return expression cannot be treated as rvalue since
36 // w is not automatic object; so copy is performed
37 return w;
38 }
39
40 Widget func_3() {
41 static Widget w;
42 // copy elision for return value not allowed since w is not
43 // automatic object; return expression cannot be treated as
44 // rvalue since w is not automatic object; so copy is performed
45 return w;
46 }
47
48 Widget g;
49 Widget func_4() {
50 // copy elision for return value not allowed since g is not
51 // automatic; cannot treat w as rvalue since not automatic
52 // object; so copy is performed
53 return g;
54 }
55
56 void func_5() {
57 Widget w;
58 // copy elision for exception object is allowed; if copy not
59 // elided, move performed
60 throw w;
61 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 720
Copy/Move/Elide Example: Widget (3)
63 void func_6(Widget w) {
64 // copy elision is not allowed since object in throw expression
65 // is function parameter; copy performed in C++17, but may become
66 // move in future (P1155)
67 throw w;
68 }
69
70 Widget func_7(Widget&& w) {
71 // copy elision is not allowed for several reasons (e.g.,
72 // cv-unqualified return type does not match cv-unqualified
73 // return-expression type, not automatic object); return
74 // expression cannot be treated as rvalue since w is not
75 // automatic object; copy performed in C++17, but may become
76 // move in future (P0527)
77 return w;
78 }
79
80 Widget func_8(Widget& w) {
81 Widget&& x = std::move(w);
82 // copy elision is not allowed for several reasons (e.g.,
83 // cv-unqualified return type does not match cv-unqualified
84 // return-expression type and not automatic object); copy
85 // performed in C++17, but may become move in future (P0527)
86 return x;
87 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 721
Copy/Move/Elide Example: Conversion (1)
1 class Widget {
2 public:
3 Widget();
4 Widget(const Widget&);
5 Widget(Widget&&);
6 // ...
7 };
8
9 class Gadget {
10 public:
11 Gadget();
12 Gadget(const Gadget&);
13 Gadget(Gadget&&);
14 Gadget(const Widget& w); // copying converting constructor
15 Gadget(Widget&& w); // moving converting constructor
16 // ...
17 };
18
19 class Doodad {
20 public:
21 Doodad();
22 Doodad(const Doodad&);
23 Doodad(Doodad&&);
24 operator Widget() const&; // copying conversion operator
25 operator Widget() &&; // moving conversion operator
26 // ...
27 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 722
Copy/Move/Elide Example: Conversion (2)
29 Gadget func_1() {
30 Widget w;
31 // copy elision is not permitted since cv-unqualified return type
32 // and cv-unqualified return-expression type do not match;
33 // when w treated as rvalue, moving converting constructor found;
34 // performs move via moving converting constructor
35 return w;
36 }
37
38 Widget func_2() {
39 Doodad t;
40 // copy elision is not permitted since cv-unqualified return type
41 // does not match cv-unqualified return-expression type;
42 // when t treated as rvalue, no constructor is found;
43 // performs copy via copying conversion operator in C++17,
44 // but may change to move in future (P1155) since some compilers
45 // do this anyways (in violation of standard)
46 return t;
47 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 723
Copy/Move/Elide Example: Slicing
1 #include <iostream>
2
3 class Base {
4 public:
5 Base();
6 Base(const Base&);
7 Base(Base&&);
8 Base& operator=(const Base&);
9 Base& operator=(Base&&);
10 // ...
11 };
12
13 class Derived : public Base {
14 public:
15 Derived();
16 Derived(const Derived&);
17 Derived(Derived&&);
18 Derived& operator=(const Derived&);
19 Derived& operator=(Derived&&);
20 // ...
21 };
22
23 Base base_1() {
24 Derived x;
25 // copy elision is not permitted since cv-unqualified
26 // return-expression type does not match cv-unqualified return
27 // type; copy is performed in C++17, but may become move in
28 // future (P1155)
29 return x;
30 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 724
Copy/Move/Elide Example: Other Operators (1)
1 class BigInt {
2 public:
3 BigInt();
4 BigInt(const BigInt&);
5 BigInt(BigInt&&);
6 BigInt& operator=(const BigInt&);
7 BigInt& operator=(BigInt&&);
8 BigInt& operator+=(int);
9 BigInt& operator++();
10 // ...
11 };
12
13 BigInt func_1(BigInt c) {
14 c += 1;
15 // copy elision for return value not allowed since return
16 // expression is function parameter; move is performed
17 return c;
18 }
19
20 BigInt func_2(BigInt c) {
21 // copy elision for return value not allowed since
22 // cv-unqualified return-expression type does not match
23 // cv-unqualified return type; copy is performed
24 return c += 1;
25 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 725
Copy/Move/Elide Example: Other Operators (2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 726
Copy/Move/Elide Example: Parentheses
1 class Widget {
2 public:
3 Widget();
4 Widget(const Widget&);
5 Widget(Widget&&);
6 // ...
7 };
8
9 Widget func_0() {
10 Widget w;
11 // copy elision is permitted
12 // if not elided, move is performed
13 return w;
14 }
15
16 Widget func_1() {
17 Widget w;
18 // standard seems to suggest copy elision not
19 // permitted since (w) is not name of object, but:
20 // Clang 7.0.0 (-std=c++17 -O2) elides copy;
21 // MSVC 19.15 (/std:c++17 /O2) elides copy;
22 // GCC 8.2 (-std=c++17 -O2) performs move
23 return (w);
24 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 727
Section 3.5
Rvalue References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 728
Section 3.5.1
Introduction
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 729
Motivation Behind Rvalue References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 730
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. [C++17 6.9.3/1]
⁓⁓⁓⁓⁓⁓⁓
■ 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–2019 Michael D. Adams C++ Version: 2019-02-04 731
Section 3.5.2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 732
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 and std::istream).
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 733
Buffer Example: Moving Versus Copying
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 734
Buffer 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, one array new, copying of element
data, 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–2019 Michael D. Adams C++ Version: 2019-02-04 735
Buffer 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–2019 Michael D. Adams C++ Version: 2019-02-04 736
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 (in concert with the rules for reference binding and
overload resolution) provide the above mechanisms.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 737
Section 3.5.3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 738
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 739
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–2019 Michael D. Adams C++ Version: 2019-02-04 740
Reference Binding
■ Again, the loss of cv qualifiers must be avoided for const and volatile
correctness.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 741
Reference Binding (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 742
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–2019 Michael D. Adams C++ Version: 2019-02-04 743
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–2019 Michael D. Adams C++ Version: 2019-02-04 744
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–2019 Michael D. Adams C++ Version: 2019-02-04 745
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–2019 Michael D. Adams C++ Version: 2019-02-04 746
Overload Resolution
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 747
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–2019 Michael D. Adams C++ Version: 2019-02-04 748
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–2019 Michael D. Adams C++ Version: 2019-02-04 749
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–2019 Michael D. Adams C++ Version: 2019-02-04 750
Why Rvalue References Cannot Bind to Lvalues (Revisited)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 751
Section 3.5.4
Moving
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 752
Move Constructors
■ Example:
class T {
public:
T();
T(const T&); // copy constructor
T(T&&); // move constructor
// ...
};
T a;
T b(std::move(a)); // calls T::T(T&&)
T c(b); // calls T::T(const T&)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 753
Move Assignment Operators
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 754
Buffer Example Revisited
■ Recall the class from earlier that represents a character buffer (whose
size is fixed at run time).
class Buffer {
public:
// ...
private:
char* data_; // pointer to buffer data
std::size_t size_; // buffer size (in characters)
};
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 755
Example Without Move Construction/Assignment
1 #include <algorithm>
2 #include <cstddef>
3
4 class Buffer {
5 public:
6 Buffer(std::size_t size, char value = 0) :
7 size_(size), data_(new char[size])
8 {std::fill_n(data_, size, value);}
9 Buffer(const Buffer& b) : size_(b.size_), data_(new char[b.size_])
10 {std::copy_n(b.data_, b.size_, data_);}
11 Buffer& operator=(const Buffer& b) {
12 if (this != &b) {
13 delete[] data_;
14 size_ = b.size_; data_ = new char[b.size_];
15 std::copy_n(b.data_, b.size_, data_);
16 }
17 return *this;
18 }
19 ˜Buffer() {delete[] data_;}
20 private:
21 char* data_; // pointer to buffer data
22 std::size_t size_; // buffer size (in characters)
23 };
24
25 Buffer getBuffer() {return Buffer(65536, ’A’);}
26
27 int main() {
28 Buffer x(0);
29 Buffer y = getBuffer(); // construct from temporary object
30 x = Buffer(32768, ’B’); // assign from temporary object
31 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 756
Example With Move Construction/Assignment
1 #include <algorithm>
2 #include <cstddef>
3 #include <utility>
4
5 class Buffer {
6 public:
7 Buffer(std::size_t size, char value = 0) :
8 size_(size), data_(new char[size])
9 {std::fill_n(data_, size, value);}
10 Buffer(const Buffer& b) : size_(b.size_), data_(new char[b.size_])
11 {std::copy_n(b.data_, b.size_, data_);}
12 Buffer& operator=(const Buffer& b) {
13 if (this != &b) {
14 delete[] data_;
15 size_ = b.size_; data_ = new char[b.size_];
16 std::copy_n(b.data_, b.size_, data_);
17 }
18 return *this;
19 }
20 // Move constructor
21 Buffer(Buffer&& b) : size_(b.size_), data_(b.data_)
22 {b.size_ = 0; b.data_ = nullptr;}
23 // Move assignment operator
24 Buffer& operator=(Buffer&& b) {
25 std::swap(size_, b.size_);
26 std::swap(data_, b.data_);
27 return *this;
28 }
29 ˜Buffer() {delete[] data_;}
30 private:
31 char* data_; // pointer to buffer data
32 std::size_t size_; // buffer size (in characters)
33 };
34
35 Buffer getBuffer() {return Buffer(65536, ’A’);}
36
37 int main() {
38 Buffer x(0);
39 Buffer y = getBuffer(); // construct from temporary object
40 x = Buffer(32768, ’B’); // assign from temporary object
41 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 757
Allowing Move Semantics in Other Contexts via std::move
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 758
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–2019 Michael D. Adams C++ Version: 2019-02-04 759
Improved Swap
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 760
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–2019 Michael D. Adams C++ Version: 2019-02-04 761
Implication of Rvalue-Reference Type Function Parameters
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 763
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–2019 Michael D. Adams C++ Version: 2019-02-04 764
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–2019 Michael D. Adams C++ Version: 2019-02-04 765
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–2019 Michael D. Adams C++ Version: 2019-02-04 766
Section 3.5.5
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 767
References to References
int i = 0;
int& & j = i; // ILLEGAL: reference to reference
■ Although one cannot directly create a reference to a reference, a
reference to a reference can arise indirectly in several contexts.
■ Typedef name:
typedef int& RefToInt;
typedef RefToInt& T; // reference to reference
■ Template function parameters:
template <class T> T func(const T& x) {return x;}
int x = 1;
func<int&>(x); // reference to reference
■ Decltype specifier:
int i = 1;
decltype((i))& j = i; // reference to reference
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 768
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–2019 Michael D. Adams C++ Version: 2019-02-04 769
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. .
[C++17 11.3.2/6]
⁓⁓⁓⁓⁓⁓⁓⁓
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:
2 An lvalue reference to any reference yields an lvalue reference.
2 An rvalue reference to an lvalue reference yields an lvalue reference.
2 An rvalue reference to an rvalue reference yields rvalue reference.
2 Any cv qualifiers applied to a reference type are discarded (since cv
qualifiers cannot be applied to a reference).
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 770
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–2019 Michael D. Adams C++ Version: 2019-02-04 771
Forwarding References
■ A cv-unqualified rvalue reference that appears in a type-deducing context
for template parameters is called a forwarding reference. [C++17 17.8.2.1/3]
⁓⁓⁓⁓⁓⁓⁓⁓⁓
■ 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–2019 Michael D. Adams C++ Version: 2019-02-04 772
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–2019 Michael D. Adams C++ Version: 2019-02-04 773
Section 3.5.6
Perfect Forwarding
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 774
Perfect Forwarding
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 775
Perfect-Forwarding Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 776
Perfect-Forwarding Example: First Failed Attempt
■ For our first attempt, we propose the following code for the (template)
function wrapper:
template <class T>
void wrapper(T p) {
func(p);
}
■ If func takes its parameter by reference, calls to wrapper and func (with
the same argument) can have different behaviors.
■ Suppose, for example, that we have the following declarations:
void func(int&); // uses pass by reference
int i;
■ Then, the following two function calls are not equivalent:
wrapper(i);
// T is deduced as int
// copy of i passed to func
// wrapper cannot change i
func(i);
// i passed by reference
// func can change i
■ Problem: The original and forwarded arguments are distinct objects.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 777
Perfect-Forwarding Example: Second Failed Attempt
■ For our second attempt, we propose the following code for the (template)
function wrapper:
template <class T>
void wrapper(T& p) {
func(p);
}
■ If, for example, the function argument is an rvalue (such as a non-string
literal or temporary object), calls to wrapper and func (with the same
argument) can have different behaviors.
■ Suppose, for example, that we have the following declaration:
void func(int); // uses pass by value
■ Then, the following two function calls are not equivalent:
wrapper(42);
// T is deduced as int
// ERROR: cannot bind rvalue to
// nonconst lvalue reference
func(42);
// OK
■ Problem: The original and forwarded arguments do not match in terms of
lvalue/rvalue-ness.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 778
Perfect-Forwarding Example: Third Failed Attempt
■ For our third attempt, we propose the following code for the (template)
function wrapper:
template <class T>
void wrapper(const T& p) {
func(p);
}
■ If, for example, the function argument is a non-const object, calls to
wrapper and func (with the same argument) will have different behaviors.
■ Suppose, for example, that we have the following declaration:
void func(int&);
int i;
■ Then, the following two function calls are not equivalent:
wrapper(i);
// ERROR: wrapper cannot call func, as this
// would discard const qualifier
func(i);
// OK
■ Problem: The original and forwarded arguments do not match in terms of
cv-qualifiers.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 779
Perfect-Forwarding Example: Solution
■ Finally, we propose the following code for the (template) function wrapper:
template <class T>
void wrapper(T&& p) {
func(static_cast<T&&>(p));
}
■ Consider now, for example, the following scenario:
int i = 42;
const int ci = i;
int& ri = i;
const int& rci = i;
wrapper(expr); // invoke wrapper
■ The parameter p is an alias for the object yielded by the expression expr.
■ The argument expr and argument to func match in terms of cv-qualifiers
and lvalue/rvalue-ness.
expr argument to func
expr Type Category T Type (T&&) Category
i int lvalue int& int& lvalue
ci const int lvalue const int& const int& lvalue
ri int& lvalue int& int& lvalue
rci const int& lvalue const int& const int& lvalue
42 int rvalue int int&& rvalue
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 780
Perfect-Forwarding Example: Solution (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 781
The std::forward Template Function
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 783
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–2019 Michael D. Adams C++ Version: 2019-02-04 784
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–2019 Michael D. Adams C++ Version: 2019-02-04 785
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–2019 Michael D. Adams C++ Version: 2019-02-04 786
Perfect-Forwarding Use Case: Emplace Operations
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 787
Other Perfect-Forwarding Examples
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 788
Section 3.5.7
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 789
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 790
References II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 791
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 792
Section 3.6
Exceptions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 793
Section 3.6.1
Introduction
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 794
Exceptions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 795
The Problem
High-Level
main
Code
Low-Level
Code
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 796
Traditional Error Handling
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 797
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–2019 Michael D. Adams C++ Version: 2019-02-04 798
Error Handling With Exceptions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 799
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–2019 Michael D. Adams C++ Version: 2019-02-04 800
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::pair(false, 0);
8 }
9 return std::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–2019 Michael D. Adams C++ Version: 2019-02-04 801
safe_divide Example: Exceptions
1 #include <iostream>
2 #include <stdexcept>
3 #include <utility>
4 #include <vector>
5
6 int safe_divide(int x, int y) {
7 if (!y) {
8 throw std::overflow_error("divide by zero");
9 }
10 return x / y;
11 }
12
13 int main() {
14 std::vector<std::pair<int, int>> v = {{10, 2}, {10, 0}};
15 for (auto p : v) {
16 try {
17 std::cout << safe_divide(p.first, p.second) <<
18 ’\n’;
19 }
20 catch(const std::overflow_error& e) {
21 std::cerr << "division by zero\n";
22 }
23 }
24 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 802
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::pair(false, 0) : std::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–2019 Michael D. Adams C++ Version: 2019-02-04 803
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–2019 Michael D. Adams C++ Version: 2019-02-04 804
Exceptions Versus Traditional Error Handling
■ advantages of exceptions:
2 exceptions allow for error handling code to be easily separated from code
that detects error
2 exceptions can easily pass error information many levels up call chain
2 passing of error information up call chain managed by language (no explicit
code required)
■ disadvantages of exceptions:
2 writing code that always behaves correctly in presence of exceptions
requires great care (as we shall see)
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 805
Section 3.6.2
Exceptions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 806
Exceptions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 807
Standard Exception Classes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 808
Standard Exception Classes (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 809
Standard Exception Classes (Continued 2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 810
Section 3.6.3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 811
Throwing Exceptions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 812
Throwing Exceptions (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 813
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–2019 Michael D. Adams C++ Version: 2019-02-04 814
Catching Exceptions (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 815
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–2019 Michael D. Adams C++ Version: 2019-02-04 816
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–2019 Michael D. Adams C++ Version: 2019-02-04 817
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–2019 Michael D. Adams C++ Version: 2019-02-04 818
Rethrowing Exceptions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 819
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–2019 Michael D. Adams C++ Version: 2019-02-04 820
Transfer of Control from Throw Site to Handler
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 821
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–2019 Michael D. Adams C++ Version: 2019-02-04 822
Another Stack Unwinding Example
1 #include <iostream>
2 #include <stdexcept>
3 #include <string>
4 #include <utility>
5
6 class Widget {
7 public:
8 Widget(const std::string& s) : s_(s) {}
9 Widget(const Widget& other) : s_(other.s_) {std::cerr << "copy ctor\n";}
10 Widget(Widget&& other) : s_(std::move(other.s_)) {std::cerr << "move ctor\n";}
11 std::string get() const {return s_;}
12 ˜Widget() {std::cerr << "dtor " << s_ << ’\n’;}
13 private:
14 std::string s_;
15 };
16
17 Widget func_1(Widget w) {
18 Widget bjarne("bjarne");
19 {
20 Widget hello("hello");
21 Widget bye("bye");
22 throw std::runtime_error("Yikes");
23 Widget bonjour("bonjour");
24 }
25 return bjarne;
26 }
27
28 int main() {
29 Widget zaphod("zaphod");
30 try {
31 Widget ford("ford");
32 Widget u = func_1(zaphod);
33 Widget arthur("arthur");
34 } catch (...) {std::cerr << "exception\n";}
35 }
■ objects destroyed during stack unwinding: bye, hello, bjarne, w, ford
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 823
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–2019 Michael D. Adams C++ Version: 2019-02-04 824
Exceptions and Construction/Destruction
■ order of construction (assuming no virtual base classes):
1 non-virtual base class objects as listed from left to right in class definition
2 non-static data members as listed from top to bottom in class definition
3 constructor body
■ order of destruction is exact reverse of order of construction, namely:
1 destructor body
2 non-static data members as listed from bottom to top in class definition
3 non-virtual base class objects as listed from right to left in class definition
■ lifetime of object begins when constructor completes
■ constructor might throw in:
2 constructor of base class object
2 constructor of data member
2 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) ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 18.3/14]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 825
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–2019 Michael D. Adams C++ Version: 2019-02-04 826
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–2019 Michael D. Adams C++ Version: 2019-02-04 827
Section 3.6.4
Exception Specifications
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 828
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)
[C++17 15.4/3]
⁓⁓⁓⁓⁓⁓⁓
■ 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–2019 Michael D. Adams C++ Version: 2019-02-04 829
The noexcept Specifier (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 830
The noexcept Specifier (Continued 2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 831
The noexcept Specifier (Continued 3)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 832
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 ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 8.2.2/4]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 834
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 <utility>
3
4 void increment(int&) noexcept;
5 char* memAlloc(std::size_t);
6
7 // does not throw exception, but not declared noexcept
8 void doesNotThrow() {};
9
10 int main() {
11 static_assert(noexcept(1 + 1) == true);
12 static_assert(noexcept(memAlloc(0)) == false);
13 // Note: does not evaluate expression
14 static_assert(noexcept(increment(*((int*)0))) == true);
15 static_assert(noexcept(increment(std::declval<int&>())) ==
16 true);
17 // Note: only uses noexcept specifiers
18 static_assert(noexcept(doesNotThrow()) == false);
19 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 835
noexcept Operator (Continued)
■ noexcept operator particularly useful for templates
■ example:
1 #include <iostream>
2 #include <type_traits>
3
4 class Int256 { /* ... */ }; // 256-bit integer
5 class BigInt { /* ... */ }; // arbitrary-precision integer
6
7 // function will not throw exception
8 Int256 operator+(const Int256& x, const Int256& y) 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_v<T>)
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–2019 Michael D. Adams C++ Version: 2019-02-04 836
Section 3.6.5
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 837
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:
2 store exception on one thread
2 then retrieve and rethrow stored exception on another thread
■ std::make_exception_ptr can be used to make exception_ptr
object
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 838
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–2019 Michael D. Adams C++ Version: 2019-02-04 839
Section 3.6.6
Exception Safety
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 840
Resource Management
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 841
Resource Leak Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 842
Cleanup
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 843
Exception Safety and Exception Guarantees
being satisfied (e.g., for std::vector, element type has nonthrowing move
or is copyable)
2 insert on std::list
■ examples of nothrow guarantee:
2 swap of two containers
2 pop_back for container
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 845
Resource Acquisition Is Initialization (RAII)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 846
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> class SmartPtr {
2 public:
3 SmartPtr(int size) : ptr_(new T[size]) {}
4 ˜SmartPtr() {delete[] ptr_;}
5 SmartPtr(SmartPtr&& other) {ptr_ = other.ptr_; other.ptr_ = nullptr;}
6 SmartPtr& operator=(SmartPtr&& other) {
7 delete[] ptr_; ptr_ = other.ptr_; other.ptr_ = nullptr;
8 return *this;
9 }
10 T* get() const {return ptr_;}
11 private:
12 T* ptr_;
13 };
14
15 void useBuffer(char* buf) { /* ... */ }
16
17 void doWork() {
18 SmartPtr<char> buf(1024);
19 useBuffer(buf.get());
20 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 847
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–2019 Michael D. Adams C++ Version: 2019-02-04 848
Other RAII Examples
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 849
Section 3.6.7
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 850
Implementation of Exception Handling
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 851
Implementation of Exception Handling (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 852
Appropriateness of Using Exceptions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 854
Section 3.6.8
Exception Gotchas
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 855
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–2019 Michael D. Adams C++ Version: 2019-02-04 857
Stack Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 858
Section 3.6.9
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 859
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 861
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.)
3 Peter Edwards. C++ Exception Handling — The Gory Details of an
Implementation. Dublin C/C++ User Group Meetup, Feb. 2018. Available
online at https://youtu.be/XpRL7exdFL8.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 862
Section 3.7
Smart Pointers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 863
Section 3.7.1
Introduction
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 864
Memory Management, Ownership, and Raw Pointers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 865
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–2019 Michael D. Adams C++ Version: 2019-02-04 866
Section 3.7.2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 867
The std::unique_ptr Template Class
■ reasonable implementation would have zero memory cost for deleter state
in case of:
2 default deleter
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 869
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–2019 Michael D. Adams C++ Version: 2019-02-04 870
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–2019 Michael D. Adams C++ Version: 2019-02-04 871
std::unique_ptr Example 1
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–2019 Michael D. Adams C++ Version: 2019-02-04 872
std::unique_ptr Example 2
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–2019 Michael D. Adams C++ Version: 2019-02-04 873
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–2019 Michael D. Adams C++ Version: 2019-02-04 874
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–2019 Michael D. Adams C++ Version: 2019-02-04 875
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–2019 Michael D. Adams C++ Version: 2019-02-04 876
Section 3.7.3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 877
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
■ shared_ptr only guarantees access to underlying control block is thread
safe (e.g., no guarantee made for accesses to 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–2019 Michael D. Adams C++ Version: 2019-02-04 878
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–2019 Michael D. Adams C++ Version: 2019-02-04 880
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–2019 Michael D. Adams C++ Version: 2019-02-04 881
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–2019 Michael D. Adams C++ Version: 2019-02-04 882
std::shared_ptr Member Functions
Modifiers
Member Name Description
reset replaced managed object
swap swaps managed objects
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 883
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
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–2019 Michael D. Adams C++ Version: 2019-02-04 884
Prefer Use of std::make_shared
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 885
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);
7
8 std::shared_ptr<int> p2(p1);
9 assert(*p2 == 0 && p2.use_count() == 2);
10
11 *p2 = 42;
12 assert(*p1 == 42);
13
14 p2.reset();
15 assert(!p2);
16 assert(*p1 == 42 && p1.use_count() == 1);
17
18 int* ip = p1.get();
19 assert(*ip == 42);;
20
21 ip = p2.get();
22 assert(ip == nullptr);
23 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 886
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–2019 Michael D. Adams C++ Version: 2019-02-04 887
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(const std::vector<int>& v_, int i_) :
8 v(v_), i(i_) {}
9 ˜Widget() {std::cout << "destructor called\n";}
10 std::vector<int> v;
11 int i;
12 };
13
14 int main() {
15 auto wp(std::make_shared<Widget>(
16 std::vector<int>{1, 2, 3}, 42));
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–2019 Michael D. Adams C++ Version: 2019-02-04 888
Example: Shared Pointer to Subobject of Managed Object
(Continued 1)
wp
p v
cb i
p
rc = 1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 889
Example: Shared Pointer to Subobject of Managed Object
(Continued 2)
wp vp
p v p
cb i cb
p
rc = 2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 890
The std::enable_shared_from_this Class Template
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 891
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–2019 Michael D. Adams C++ Version: 2019-02-04 892
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–2019 Michael D. Adams C++ Version: 2019-02-04 893
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–2019 Michael D. Adams C++ Version: 2019-02-04 894
Section 3.7.4
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 895
std::shared_ptr and Circular References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 896
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–2019 Michael D. Adams C++ Version: 2019-02-04 897
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–2019 Michael D. Adams C++ Version: 2019-02-04 898
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–2019 Michael D. Adams C++ Version: 2019-02-04 899
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–2019 Michael D. Adams C++ Version: 2019-02-04 900
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–2019 Michael D. Adams C++ Version: 2019-02-04 901
The std::weak_ptr Template Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 902
std::weak_ptr Member Functions
Modifiers
Member Name Description
reset replaced managed object
swap swaps managed objects
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 903
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–2019 Michael D. Adams C++ Version: 2019-02-04 904
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–2019 Michael D. Adams C++ Version: 2019-02-04 906
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–2019 Michael D. Adams C++ Version: 2019-02-04 907
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–2019 Michael D. Adams C++ Version: 2019-02-04 908
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–2019 Michael D. Adams C++ Version: 2019-02-04 909
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–2019 Michael D. Adams C++ Version: 2019-02-04 910
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–2019 Michael D. Adams C++ Version: 2019-02-04 911
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–2019 Michael D. Adams C++ Version: 2019-02-04 912
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–2019 Michael D. Adams C++ Version: 2019-02-04 913
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–2019 Michael D. Adams C++ Version: 2019-02-04 914
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–2019 Michael D. Adams C++ Version: 2019-02-04 915
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–2019 Michael D. Adams C++ Version: 2019-02-04 916
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–2019 Michael D. Adams C++ Version: 2019-02-04 917
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–2019 Michael D. Adams C++ Version: 2019-02-04 918
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
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 919
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–2019 Michael D. Adams C++ Version: 2019-02-04 920
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
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 921
Avoiding Circular References Example (Continued 14)
root
p
cb
p
rc = 0
wc = 1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 922
Avoiding Circular References Example (Continued 15)
root
p
cb
p
rc = 0
wc = 0
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 923
Avoiding Circular References Example (Continued 16)
root
p
cb
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 924
Avoiding Circular References Example (Continued 17)
root
p
cb
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 926
The boost::intrusive_ptr Class Template
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 927
The boost::intrusive_ptr Class Template (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 928
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–2019 Michael D. Adams C++ Version: 2019-02-04 929
Section 3.7.6
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 930
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–2019 Michael D. Adams C++ Version: 2019-02-04 931
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:
2 is optional (e.g., is not always used or is lazily initialized)
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 932
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 std::unique_ptr<Element[]> array_;
13 std::size_t size_;
14 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 933
Pimpl Idiom
■ with pimpl
. . . . . . .idiom,
. . . . . interface and implementation split across two classes,
namely, handle class and implementation class
■ handle object has pointer that owns implementation object
1 #include <experimental/propagate_const>
2 #include <memory>
3
4 class Widget {
5 public:
6 // ... (member functions that forward calls to
7 // implementation object)
8 private:
9 class WidgetImpl; // implementation class defined elsewhere
10 std::experimental::propagate_const<std::unique_ptr<
11 WidgetImpl>> impl_;
12 // incomplete type WidgetImpl is allowed
13 // ...
14 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 934
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–2019 Michael D. Adams C++ Version: 2019-02-04 935
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–2019 Michael D. Adams C++ Version: 2019-02-04 936
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–2019 Michael D. Adams C++ Version: 2019-02-04 937
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
■ care needed for destruction, otherwise may overflow stack
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_; // non-owning pointers
12 // ...
13 };
14 private:
15 std::vector<std::shared_ptr<Node>> roots_; // owning pointers
16 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 938
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–2019 Michael D. Adams C++ Version: 2019-02-04 939
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::scoped_lock<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–2019 Michael D. Adams C++ Version: 2019-02-04 940
Section 3.7.7
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 941
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 942
Section 3.8
Memory Management
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 943
Memory Management
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 944
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–2019 Michael D. Adams C++ Version: 2019-02-04 945
Alignment
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 946
The alignof Operator
■ object type can have restriction on address at which object of type can
start called alignment requirement ⁓⁓⁓⁓⁓⁓⁓
[C++17 6.11/1]
■ 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–2019 Michael D. Adams C++ Version: 2019-02-04 947
The alignas Specifier
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 948
Section 3.8.1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 949
New Expressions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 951
New Expressions and Allocation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 952
Allocation Function Overload Resolution
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 953
Allocation Function Overload Resolution (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 954
New Expressions and Deallocation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 955
Delete Expressions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 957
Delete Expressions (Continued 2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 958
Operator New (i.e., operator new)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 960
Operator New Overloads (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 961
Operator New Examples
1 #include <new>
2 #include <cassert>
3 #include <string>
4
5 void func_1() {
6 // allocating operator new
7 std::string* sp = static_cast<std::string*>(
8 ::operator new(sizeof(std::string)));
9 // allocation succeeded since no exception thrown
10 assert(sp);
11 // ... (deallocate memory)
12 }
13
14 void func_2() {
15 // allocating and non-throwing operator new
16 std::string* sp = static_cast<std::string*>(
17 ::operator new(sizeof(std::string), std::nothrow));
18 // sp may be null since allocation might have failed
19 // ... (deallocate memory)
20 }
21
22 void func_3() {
23 int i;
24 // non-allocating operator new
25 int* ip = static_cast<int*>(::operator new(sizeof(int),
26 static_cast<void*>(&i)));
27 assert(ip == &i);
28 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 962
Operator Array New (i.e., operator new[])
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 963
Operator Array New Overloads
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 964
Operator Array New Overloads (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 965
Operator Array New Examples
1 #include <new>
2 #include <cassert>
3 #include <string>
4
5 void func_1() {
6 // allocating operator array new
7 std::string* sp = static_cast<std::string*>(
8 ::operator new[](1000 * sizeof(std::string)));
9 // allocation succeeded since no exception thrown
10 assert(sp);
11 // ... (deallocate)
12 }
13
14 void func_2() {
15 std::string* sp = static_cast<std::string*>(
16 ::operator new[](1000 * sizeof(std::string), std::nothrow));
17 // sp may be null since allocation might have failed
18 // ... (deallocate)
19 }
20
21 void func_3() {
22 static int a[1000];
23 int* ip = static_cast<int*>(::operator new[](1000 * sizeof(int),
24 static_cast<void*>(a)));
25 assert(ip == a);
26 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 966
Operator Delete (i.e., operator delete)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 967
Operator Delete Overloads
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 968
Operator Delete Examples
1 #include <new>
2 #include <cassert>
3 #include <string>
4
5 void func_1() {
6 // allocating operator new
7 std::string* sp = static_cast<std::string*>(
8 ::operator new(sizeof(std::string)));
9 // allocation succeeded since no exception thrown
10 assert(sp);
11 ::operator delete(sp);
12 }
13
14 void func_2() {
15 // allocating and non-throwing operator new
16 std::string* sp = static_cast<std::string*>(
17 ::operator new(sizeof(std::string), std::nothrow));
18 // sp may be null since allocation might have failed
19 // deleting null pointer is allowed
20 ::operator delete(sp);
21 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 969
Operator Array Delete (i.e., operator delete[])
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 970
Operator Array Delete Overloads
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 971
Operator Array Delete Examples
1 #include <new>
2 #include <cassert>
3 #include <string>
4
5 void func_1() {
6 // allocating operator array new
7 std::string* sp = static_cast<std::string*>(
8 ::operator new[](1000 * sizeof(std::string)));
9 // allocation succeeded since no exception thrown
10 assert(sp);
11 ::operator delete[](sp);
12 }
13
14 void func_2() {
15 std::string* sp = static_cast<std::string*>(
16 ::operator new[](1000 * sizeof(std::string), std::nothrow));
17 // sp may be null since allocation might have failed
18 // deleting null pointer is allowed
19 ::operator delete[](sp);
20 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 972
Replacing Operator New and Operator Delete
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 973
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–2019 Michael D. Adams C++ Version: 2019-02-04 974
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–2019 Michael D. Adams C++ Version: 2019-02-04 975
Placement New
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 976
Placement New Examples
1 #include <new>
2 #include <vector>
3 #include <string>
4 #include <cassert>
5 #include <utility>
6
7 void func_1() {
8 alignas(int) char buffer[sizeof(int)];
9 int* ip = ::new (static_cast<void*>(buffer)) int(42);
10 assert(static_cast<void*>(ip) == buffer && *ip == 42);
11 }
12
13 void func_2() {
14 alignas(std::string) char buffer[sizeof(std::string)];
15 std::string* vp = ::new (static_cast<void*>(buffer)) std::string("hello");
16 assert(static_cast<void*>(vp) == buffer && vp->size() == 5 &&
17 (*vp)[0] == ’h’);
18 // ... (destroy)
19 }
20
21 template <class T, class... Args> T* construct_at(void* ptr, Args&&... args)
22 {return ::new (ptr) T(std::forward<Args>(args)...);}
23
24 void func_3() {
25 alignas(std::vector<int>) char buffer[sizeof(std::vector<int>)];
26 std::vector<int>* vp = construct_at<std::vector<int>>(buffer, 1000, 42);
27 assert(static_cast<void*>(vp) == buffer && vp->size() == 1000 &&
28 (*vp)[0] == 42);
29 // ... (destroy vector)
30 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 977
Direct Destructor Invocation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 978
Pseudodestructors
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 980
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–2019 Michael D. Adams C++ Version: 2019-02-04 981
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–2019 Michael D. Adams C++ Version: 2019-02-04 982
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–2019 Michael D. Adams C++ Version: 2019-02-04 983
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–2019 Michael D. Adams C++ Version: 2019-02-04 984
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 valid_ = false;
15 reinterpret_cast<T*>(&storage_)->˜T();
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–2019 Michael D. Adams C++ Version: 2019-02-04 985
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–2019 Michael D. Adams C++ Version: 2019-02-04 986
Handling Uninitialized Storage
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 987
Functions for Uninitialized Storage
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 988
Functions for Uninitialized Storage (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 989
Some Example Implementations
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 990
Bounded Array Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 991
Bounded Array Example: aligned_buffer.hpp
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 992
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–2019 Michael D. Adams C++ Version: 2019-02-04 993
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 static_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 static_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) { // self-assignment check of questionable value
55 clear();
56 finish_ = std::uninitialized_move(other.buf_.start(), other.finish_,
57 buf_.start());
58 }
59 return *this;
60 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 994
Bounded Array Example: array.hpp (3)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 995
Bounded Array Example: array.hpp (4)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 996
Vector Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 997
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() noexcept : 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–2019 Michael D. Adams C++ Version: 2019-02-04 998
Vector Example: vec.hpp (2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 999
Vector Example: vec.hpp (3)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1000
Vector Example: vec.hpp (4)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1001
Vector Example: vec.hpp (5)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1002
Vector Example: vec.hpp (6)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1003
Section 3.8.3
Allocators
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1004
Allocators
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1005
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–2019 Michael D. Adams C++ Version: 2019-02-04 1006
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–2019 Michael D. Adams C++ Version: 2019-02-04 1007
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:
2 improved efficiency (e.g., better locality and less contention)
2 debugging
2 performance analysis (e.g., collecting statistics on memory allocation)
2 testing (e.g., forcing allocation failures)
2 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:
2 stack-based allocation
2 per-container allocation
2 per-thread allocation (which avoids synchronization issues)
2 pooled allocation
2 arena allocation
■ may want to handle relocatable data (e.g., shared memory)
■ may want to use memory mapped files
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1008
Examples of Allocators
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1009
Allocators
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1010
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:
2 type T of object for which allocator manages (i.e., allocates and
deallocates) memory
■ pointer:
2 pointer type used to refer to storage obtained from allocator (not
necessarily T*)
2 optional: default of T* provided by allocator_traits
■ const_pointer:
2 const version of pointer
2 optional: default of const T* provided by allocator_traits
■ pointer allocate(size_type n):
2 allocate storage suitable for n objects of type T
■ void deallocate(pointer ptr, size_type n):
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1011
Allocator Members (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1012
Remarks on Allocators
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1013
Malloc-Based Allocator: Allocator Code
1 #include <cstdlib>
2 #include <limits>
3 #include <new>
4
5 template <class T> 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> T* mallocator<T>::allocate(std::size_t n) const {
18 if (!n) {return nullptr;}
19 if (n > std::numeric_limits<std::size_t>::max() / sizeof(T))
20 {throw std::bad_array_new_length();}
21 void* p = std::malloc(n * sizeof(T));
22 if (!p) {throw std::bad_alloc();}
23 return static_cast<T*>(p);
24 }
25
26 template <class T> void mallocator<T>::deallocate(T* p, std::size_t)
27 const noexcept
28 {std::free(p);}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1014
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–2019 Michael D. Adams C++ Version: 2019-02-04 1015
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:
2 when container copy/move constructed, what allocator does new container
receive?
2 when container copy/move assigned, what allocator does
copied-to/moved-to container receive?
2 when containers swapped, what allocator does each container receive?
■ deep propagation refers to propagation of allocator from parent container
to its descendants in hierarchy of nested containers:
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1016
New-Based Allocator
1 #include <limits>
2 #include <new>
3 #include <type_traits>
4
5 template <class T> 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> T* allocator<T>::allocate(std::size_t n) {
19 if (n > std::numeric_limits<std::size_t>::max() / sizeof(T))
20 {throw std::bad_array_new_length();}
21 return static_cast<T*>(::operator new(n * sizeof(T)));
22 }
23
24 template <class T, class U> inline bool operator==(const allocator<T>&,
25 const allocator<U>&) noexcept
26 {return true;}
27
28 template <class T, class U> inline bool operator!=(const allocator<T>&,
29 const allocator<U>&) noexcept
30 {return false;}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1017
Fixed-Size Arena Allocator: Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1018
Fixed-Size Arena Allocator: Code (1)
1 #include <cstddef>
2 #include <limits>
3 #include <memory>
4 #include <new>
5
6 template <std::size_t N, std::size_t Align = alignof(std::max_align_t)>
7 class arena {
8 public:
9 arena() : ptr_(buf_) {}
10 arena(const arena&) = delete;
11 arena& operator=(const arena&) = delete;
12 ˜arena() = default;
13 constexpr std::size_t alignment() const {return Align;}
14 constexpr std::size_t capacity() const {return N;}
15 constexpr std::size_t used() const {return ptr_ - buf_;}
16 constexpr std::size_t free() const {return N - used();}
17 template <std::size_t ReqAlign> void* allocate(std::size_t n);
18 void deallocate(void* ptr, std::size_t n) {}
19 void clear() {ptr_ = buf_;}
20 private:
21 template <std::size_t ReqAlign>
22 static char* align(char* ptr, std::size_t n, std::size_t max);
23 alignas(Align) char buf_[N]; // storage buffer
24 char* ptr_; // pointer to first unused byte
25 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1019
Fixed-Size Arena Allocator: Code (2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1020
Fixed-Size Arena Allocator: Code (3)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1021
Fixed-Size Arena Allocator: Code (4)
70 private:
71 template <class T1, std::size_t N1, std::size_t A1, class T2,
72 std::size_t N2, std::size_t A2>
73 friend bool operator==(const salloc<T1, N1, A1>&,
74 const salloc<T2, N2, A2>&);
75 template <class, std::size_t, std::size_t> friend class salloc;
76 arena_type* a_; // arena from which to allocate storage
77 };
78
79 template <class T1, std::size_t N1, std::size_t A1, class T2, std::size_t N2,
80 std::size_t A2>
81 inline bool operator==(const salloc<T1, N1, A1>& a,
82 const salloc<T2, N2, A2>& b)
83 {return N1 == N2 && A1 == A2 && a.a_ == b.a_;}
84
85 template <class T1, std::size_t N1, std::size_t A1, class T2, std::size_t N2,
86 std::size_t A2>
87 inline bool operator!=(const salloc<T1, N1, A1>& a,
88 const salloc<T2, N2, A2>& b)
89 {return !(a == b);}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1022
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–2019 Michael D. Adams C++ Version: 2019-02-04 1023
Allocator-Aware Containers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1024
The std::allocator_traits Class Template
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1025
Lateral Allocator Propagation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1027
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–2019 Michael D. Adams C++ Version: 2019-02-04 1028
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–2019 Michael D. Adams C++ Version: 2019-02-04 1029
Optional Value Example: Code (2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1030
Optional Value Example: Code (3)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1031
Optional Value Example: Code (4)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1032
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 using std::swap;
101 if (this != &other) {
102 if constexpr (traits::propagate_on_container_move_assignment::value) {
103 clear();
104 swap(alloc_(), other.alloc_());
105 swap(value_, other.value_);
106 } else if (alloc_() == other.alloc_()) {
107 clear();
108 swap(value_, other.value_);
109 } else {
110 pointer p = copy_(alloc_(), other.value_);
111 swap(value_, other.value_);
112 other.clear();
113 value_ = p;
114 }
115 }
116 return *this;
117 }
118
119 template <class T, class Alloc>
120 void optval<T, Alloc>::swap(optval& other) noexcept {
121 using std::swap;
122 assert(traits::propagate_on_container_swap::value ||
123 alloc_() == other.alloc_());
124 if constexpr (traits::propagate_on_container_swap::value)
125 {swap(alloc_(), other.alloc_());}
126 swap(value_, other.value_);
127 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1033
Optional Value Example: Code (6)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1034
The std::scoped_allocator_adaptor Class Template
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1036
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–2019 Michael D. Adams C++ Version: 2019-02-04 1037
Section 3.8.4
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1038
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1039
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1040
Talks II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1041
Talks III
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1042
Section 3.9
Concurrency
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1043
Section 3.9.1
Preliminaries
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1044
Processors
Processor
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1046
Memory Hierarchy
Core L1 L2 LL Main Bulk
Excluding ···
Cache Cache Cache Cache Memory Storage
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1049
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–2019 Michael D. Adams C++ Version: 2019-02-04 1050
Section 3.9.2
Multithreaded Programming
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1051
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–2019 Michael D. Adams C++ Version: 2019-02-04 1052
Why Multithreading?
■ Keep all of the processor cores busy (i.e., fully utilize all cores).
2 Most modern systems have multiple processor cores, due to having either
multiple processors or a single processor that is multicore.
2 A single thread cannot fully utilize the computational resources available in
such systems.
■ Keep processes responsive.
2 In graphics applications, keep the GUI responsive while the application is
performing slow operations such as I/O.
2 In network server applications, keep the server responsive to new
connections while handling already established ones.
■ Simplify the coding of cooperating tasks.
2 Some programs consist of several logically distinct tasks.
2 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.
2 For certain types of applications, multithreading can significantly reduce the
conceptual complexity of the program.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1053
Section 3.9.3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1054
Memory Model
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1055
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:
2 x = 1; y = 1; b = x; a = y;
2 y = 1; x = 1; a = y; b = x;
2 x = 1; a = y; y = 1; b = x;
2 y = 1; b = x; x = 1; a = y;
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1056
Sequential-Consistency (SC) Memory Model
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1058
Load/Store Reordering Example: Multiple Threads
■ Consider the addition of a second thread to the program to yield the code
below.
Original Thread 1 Code Thread 2 Code
x = 1; if (y == 1) {
y = 1; assert(x == 1);
// ... }
■ 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–2019 Michael D. Adams C++ Version: 2019-02-04 1059
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;
■ Some possible sequentially-consistent executions of the program include:
2 x = 1; y = 1; b = x; a = y; (a is 1, b is 1)
2 y = 1; x = 1; a = y; b = x; (a is 1, b is 1)
2 x = 1; a = y; y = 1; b = x; (a is 0, b is 1)
2 y = 1; b = x; x = 1; a = y; (a is 1, b is 0)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1060
Store-Buffer Example: Store Buffer
Processor Memory
Register x
(2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1061
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–2019 Michael D. Adams C++ Version: 2019-02-04 1062
Atomicity of Memory Operations
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1063
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):
2 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;
2 The program has data races on both x and y.
2 Since z is not modified by any thread, z cannot participate in a data race.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1064
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:
2 thread 1 reads x; and
2 thread 2 writes 5678 (hexadecimal) to x.
■ 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:
2 thread 1 writes 1234 (hexadecimal) to x; and
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1066
SC Data-Race Free (SC-DRF) Memory Model
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1068
Section 3.9.4
Thread Management
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1069
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 ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 33.3.2/1]
■ all unjoinable thread objects have same ID, distinct from ID of every
joinable thread object ⁓⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 33.3.2.1/1]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1070
The std::thread Class (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1071
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–2019 Michael D. Adams C++ Version: 2019-02-04 1072
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–2019 Michael D. Adams C++ Version: 2019-02-04 1073
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–2019 Michael D. Adams C++ Version: 2019-02-04 1074
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–2019 Michael D. Adams C++ Version: 2019-02-04 1075
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–2019 Michael D. Adams C++ Version: 2019-02-04 1076
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–2019 Michael D. Adams C++ Version: 2019-02-04 1077
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–2019 Michael D. Adams C++ Version: 2019-02-04 1078
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–2019 Michael D. Adams C++ Version: 2019-02-04 1079
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–2019 Michael D. Adams C++ Version: 2019-02-04 1080
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–2019 Michael D. Adams C++ Version: 2019-02-04 1081
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–2019 Michael D. Adams C++ Version: 2019-02-04 1082
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–2019 Michael D. Adams C++ Version: 2019-02-04 1083
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–2019 Michael D. Adams C++ Version: 2019-02-04 1084
The std::thread Class and Exception Safety (Continued)
scoped_thread_1_1.hpp
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() {if (joinable()) {join();}}
15 };
main.cpp
1 #include <vector>
2 #include <unistd.h>
3 #include "scoped_thread_1_1.hpp"
4
5 void worker() {sleep(1);}
6
7 int main() {
8 std::vector<scoped_thread> threads;
9 for (int i = 0; i < 16; ++i) {threads.emplace_back(worker);}
10 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1085
Section 3.9.5
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1086
Shared Data
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1088
Critical Sections
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1089
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–2019 Michael D. Adams C++ Version: 2019-02-04 1090
Example: Data Race (Counter)
1 #include <iostream>
2 #include <thread>
3
4 unsigned long long counter = 0;
5
6 void func() {
7 for (unsigned long long i = 0; i < 1’000’000; ++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–2019 Michael D. Adams C++ Version: 2019-02-04 1091
Example: Data Race and/or Race Condition (IntSet)
1 #include <iostream>
2 #include <thread>
3 #include <unordered_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::unordered_set<int> s_;
13 };
14
15 IntSet s;
16
17 int main() {
18 std::thread t1([](){
19 for (int i = 0; i < 10’000; ++i) {s.add(2 * i);}
20 });
21 std::thread t2([](){
22 for (int i = 0; i < 10’000; ++i) {s.add(2 * i + 1);}
23 });
24 t1.join(); t2.join();
25 std::cout << s.contains(1000) << ’\n’;
26 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1092
Section 3.9.6
Mutexes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1093
Mutexes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1094
The std::mutex Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1095
std::mutex Members
Member Types
Name Description
native_handle_type system-dependent handle type for underlying mu-
tex entity
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1096
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 (unsigned long long i = 0; i < 1’000’000; ++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–2019 Michael D. Adams C++ Version: 2019-02-04 1097
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–2019 Michael D. Adams C++ Version: 2019-02-04 1098
std::scoped_lock Members
Member Types
Name Description
mutex_type underlying mutex type if only one
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1099
Example: Avoiding Data Race Using Mutex (Counter) (scoped_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 (unsigned long long i = 0; i < 1’000’000; ++i) {
10 // scoped_lock constructor acquires mutex
11 std::scoped_lock lock(m);
12 ++counter;
13 // scoped_lock 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–2019 Michael D. Adams C++ Version: 2019-02-04 1100
Example: Avoiding Data Race Using Mutex (IntSet) (scoped_lock)
1 #include <iostream>
2 #include <mutex>
3 #include <thread>
4 #include <unordered_set>
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–2019 Michael D. Adams C++ Version: 2019-02-04 1101
Example: Acquiring Two Locks for Swap (Incorrect)
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 long size() {return 16 * 1024L * 1024L;}
10 BigBuf() : data_(size()) {}
11 BigBuf& operator=(const BigBuf&) = delete;
12 BigBuf& operator=(BigBuf&&) = delete;
13 void swap(BigBuf& other) {
14 if (this == &other) {return;}
15 // acquiring the two mutexes in this way can result in deadlock
16 std::scoped_lock lock1(m_);
17 std::scoped_lock 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 std::thread t1([](){
31 for (long i = 0; i < 1’000’000; ++i) a.swap(b);
32 });
33 std::thread t2([](){
34 for (long i = 0; i < 1’000’000; ++i) b.swap(a);
35 });
36 t1.join(); t2.join();
37 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1102
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 long size() {return 16 * 1024L * 1024L;}
10 BigBuf() : data_(size()) {}
11 BigBuf& operator=(const BigBuf&) = delete;
12 BigBuf& operator=(BigBuf&&) = delete;
13 void swap(BigBuf& other) {
14 if (this == &other) {return;}
15 std::scoped_lock sl(m_, other.m_);
16 std::swap(data_, other.data_);
17 }
18 // ...
19 private:
20 std::vector<char> data_;
21 mutable std::mutex m_;
22 };
23
24 BigBuf a;
25 BigBuf b;
26
27 int main() {
28 std::thread t1([](){
29 for (long i = 0; i < 1’000’000; ++i) a.swap(b);
30 });
31 std::thread t2([](){
32 for (long i = 0; i < 1’000’000; ++i) b.swap(a);
33 });
34 t1.join(); t2.join();
35 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1103
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::scoped_lock, 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 ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 33.4.4.3]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1104
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–2019 Michael D. Adams C++ Version: 2019-02-04 1105
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–2019 Michael D. Adams C++ Version: 2019-02-04 1106
Example: Avoiding Data Race Using Mutex (unique_lock)
1 #include <iostream>
2 #include <thread>
3 #include <mutex>
4
5 unsigned long counter = 0;
6 std::mutex m;
7
8 void func() {
9 for (unsigned long i = 0; i < 1’000’000; ++i) {
10 // Create a lock object without acquiring the mutex.
11 std::unique_lock lock(m, std::defer_lock);
12 // ...
13 lock.lock(); // Acquire the mutex.
14 ++counter;
15 lock.unlock(); // Release the mutex.
16 // ...
17 lock.lock(); // Acquire the mutex.
18 ++counter;
19 lock.unlock(); // Release the mutex.
20 // ...
21 // The unique_lock destructor releases the mutex (if held).
22 }
23 }
24
25 int main() {
26 std::thread t1(func);
27 std::thread t2(func);
28 t1.join();
29 t2.join();
30 std::cout << counter << ’\n’;
31 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1107
The std::lock Template Function
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1108
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 long size() {return 16 * 1024L * 1024L;}
10 BigBuf() : data_(size()) {}
11 BigBuf& operator=(const BigBuf&) = delete;
12 BigBuf& operator=(BigBuf&&) = delete;
13 void swap(BigBuf& other) {
14 if (this == &other) {return;}
15 std::unique_lock lock1(m_, std::defer_lock);
16 std::unique_lock lock2(other.m_, std::defer_lock);
17 std::lock(lock1, lock2);
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 std::thread t1([](){
31 for (long i = 0; i < 1’000’000; ++i) a.swap(b);
32 });
33 std::thread t2([](){
34 for (long i = 0; i < 1’000’000; ++i) b.swap(a);
35 });
36 t1.join(); t2.join();
37 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1109
The std::timed_mutex Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1110
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–2019 Michael D. Adams C++ Version: 2019-02-04 1111
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–2019 Michael D. Adams C++ Version: 2019-02-04 1112
Recursive Mutex Classes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1113
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:
2 Only one thread can hold an exclusive lock on a mutex.
2 While a thread holds an exclusive lock on a mutex, no other thread can hold
any type of lock on the mutex.
■ Shared lock:
2 Any number of threads (within implementation limits) can take a shared
lock on a mutex.
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1114
The std::shared_mutex Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1115
std::shared_mutex Members
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1116
std::shared_mutex Members (Continued)
Other Functions
Name Description
native_handle get handle for underlying mutex entity
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1117
The std::shared_lock Template Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1118
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::scoped_lock 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::scoped_lock 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–2019 Michael D. Adams C++ Version: 2019-02-04 1119
The std::shared_timed_mutex Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1120
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::scoped_lock 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::scoped_lock 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–2019 Michael D. Adams C++ Version: 2019-02-04 1121
std::once_flag and std::call_once
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1122
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–2019 Michael D. Adams C++ Version: 2019-02-04 1123
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–2019 Michael D. Adams C++ Version: 2019-02-04 1124
Static Local Variable Initialization and Thread Safety
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1125
Section 3.9.7
Condition Variables
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1126
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, for example,
another thread may have caused the condition to change or a spurious
awakening may have occurred.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1127
The std::condition_variable Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1128
std::condition_variable Members
Member Types
Name Description
native_handle_type system-dependent handle type for underlying con-
dition variable entity
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1129
std::condition_variable Members (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1130
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::scoped_lock 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–2019 Michael D. Adams C++ Version: 2019-02-04 1131
The std::condition_variable_any Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1132
Thread Pools
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1134
Section 3.9.8
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1135
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–2019 Michael D. Adams C++ Version: 2019-02-04 1136
Promises and Futures (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1137
The std::promise Template Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1138
std::promise Members
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1139
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–2019 Michael D. Adams C++ Version: 2019-02-04 1140
The std::future Template Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1141
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–2019 Michael D. Adams C++ Version: 2019-02-04 1142
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–2019 Michael D. Adams C++ Version: 2019-02-04 1143
The std::shared_future Template Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1144
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–2019 Michael D. Adams C++ Version: 2019-02-04 1145
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–2019 Michael D. Adams C++ Version: 2019-02-04 1146
The std::async Template Function (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1147
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–2019 Michael D. Adams C++ Version: 2019-02-04 1148
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–2019 Michael D. Adams C++ Version: 2019-02-04 1149
The std::packaged_task Template Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1150
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–2019 Michael D. Adams C++ Version: 2019-02-04 1151
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–2019 Michael D. Adams C++ Version: 2019-02-04 1152
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–2019 Michael D. Adams C++ Version: 2019-02-04 1153
Section 3.9.9
Atomics
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1154
Atomics
2 std::atomic
■ These types provide a uniform interface for accessing the atomic memory
operations of the underlying hardware.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1155
Atomics (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1156
The std::atomic_flag Class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1157
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–2019 Michael D. Adams C++ Version: 2019-02-04 1158
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::scoped_lock 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–2019 Michael D. Adams C++ Version: 2019-02-04 1159
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–2019 Michael D. Adams C++ Version: 2019-02-04 1160
The std::atomic Template Class
■ std::atomic class provides types with atomic operations
■ declaration:
template <class T> struct atomic;
■ provides object of type T with atomic operations
■ has partial specializations for integral types and pointer types
■ full specializations for all fundamental types
■ in order to use class type for T, T must be trivially copyable [C++17 32.6/1] and
⁓⁓⁓⁓⁓⁓⁓
bitwise equality comparable
■ not required to be lock free
■ on most popular platforms atomic is lock free when T is built-in type
■ not move constructible and not copy constructible
■ assignable but assignment operator returns value not reference
■ most operations have memory order argument
■ default memory order is SC (std::memory_order_seq_cst)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1161
std::atomic Members
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–2019 Michael D. Adams C++ Version: 2019-02-04 1162
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–2019 Michael D. Adams C++ Version: 2019-02-04 1163
std::atomic Members (Continued 2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1164
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–2019 Michael D. Adams C++ Version: 2019-02-04 1165
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–2019 Michael D. Adams C++ Version: 2019-02-04 1166
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–2019 Michael D. Adams C++ Version: 2019-02-04 1167
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–2019 Michael D. Adams C++ Version: 2019-02-04 1168
An Obligatory Note on volatile
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1169
Section 3.9.10
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1170
Semantics of Multithreaded Programs
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1171
Happens-Before Relationships
■ The value read for x in operation B will not necessarily be 1, since the
result of A may not yet be visible to thread 2 (e.g., due to caching).
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1173
Sequenced-Before Relationships
■ Given two operations A and B performed in the same thread, the
operation A is sequenced before B if A precedes B in program order (i.e.,
source-code order).
■ Sequenced-before relationships are transitive (i.e., if A is sequenced
before B, and B is sequenced before C, then A is sequenced before C).
■ Example: In the code below, statement A is sequenced before
statement B; B is sequenced before statement C; and, by transitivity, A is
sequenced before C.
x = 1; // A
y = 2; // B
z = x + 1; // C
■ Example:
2 Consider the line of code below, which performs (in order) the following
operations: 1) multiplication, 2) addition, and 3) assignment.
y = a * x + b; // (y = ((a * x) + b);
2 Multiplication is sequenced before addition.
2 Addition is sequenced before assignment.
2 Thus, by transitivity, multiplication is sequenced before assignment.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1174
Sequenced-Before Relationships (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1175
Inter-Thread Happens-Before Relationships
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1176
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–2019 Michael D. Adams C++ Version: 2019-02-04 1177
Synchronizes-With Relationships
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1178
Examples of Synchronizes-With Relationships
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1179
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–2019 Michael D. Adams C++ Version: 2019-02-04 1180
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–2019 Michael D. Adams C++ Version: 2019-02-04 1181
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:
2 sequentially consistent (std::memory_order_seq_cst)
2 acquire-release (std::memory_order_acq_rel)
2 acquire (std::memory_order_acquire)
2 release (std::memory_order_release)
2 consume (std::memory_order_consume)
2 relaxed (std::memory_order_relaxed)
simultaneously (i.e., total order for all writes to all atomic objects); and
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1183
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–2019 Michael D. Adams C++ Version: 2019-02-04 1184
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–2019 Michael D. Adams C++ Version: 2019-02-04 1185
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–2019 Michael D. Adams C++ Version: 2019-02-04 1186
Modification Order Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1187
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–2019 Michael D. Adams C++ Version: 2019-02-04 1188
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–2019 Michael D. Adams C++ Version: 2019-02-04 1189
Sequentially-Consistent Model
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1190
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–2019 Michael D. Adams C++ Version: 2019-02-04 1191
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–2019 Michael D. Adams C++ Version: 2019-02-04 1192
Acquire-Release Model
■ For the acquire-release model, the memory order is chosen as follows:
2 a read operation uses the acquire order (std::memory_order_acquire)
2 a write operation uses the release order (std::memory_order_release)
2 a read-modify-write operation uses one of the orders allowed for read and
write operations, or the acquire-release order
(std::memory_order_acq_rel), which results in read acquire and write
release.
■ No total ordering exists on all writes to all atomic objects (unlike in the
sequentially-consistent model).
■ Consequently, threads do not necessarily have to agree on the relative
order in which different atomics objects are modified.
■ A write-release operation W on an atomic object M synchronizes with a
read-acquire operation on M that reads the value written by W (or a value
written by the release sequence headed by W ). [C++17 32.4/2]
⁓⁓⁓⁓⁓⁓⁓
■ The acquire-release model is useful for situations that involve pairwise
synchronization of threads, such as with mutexes.
■ With the acquire-release model, it is often still possible to reason about
code behavior without too much difficulty.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1193
Example: Acquire-Release 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_release);
■ thread 2 code (writes y):
y.store(1, std::memory_order_release);
■ thread 3 code (reads x then y):
int x1 = x.load(std::memory_order_acquire);
int y1 = y.load(std::memory_order_acquire);
■ thread 4 code (reads y then x):
int y2 = y.load(std::memory_order_acquire);
int x2 = x.load(std::memory_order_acquire);
■ no ordering relationship between stores to x and y
■ so, thread 3 and thread 4 do not need to agree about order in which x and
y are modified
■ possible to see x1 == 1 and y1 == 0 in thread 3 (i.e., thread 3 sees x
change before y) and x2 == 0 and y2 == 1 in thread 4 (i.e., thread 4
sees y change before x)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1194
Example: Acquire-Release 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_release);}
8
9 void w_y() {y.store(1, std::memory_order_release);}
10
11 void r_xy() {
12 while (!x.load(std::memory_order_acquire)) {}
13 if (y.load(std::memory_order_acquire)) {++c;}
14 }
15
16 void r_yx() {
17 while (!y.load(std::memory_order_acquire)) {}
18 if (x.load(std::memory_order_acquire)) {++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–2019 Michael D. Adams C++ Version: 2019-02-04 1195
Example: Spinlock Mutex Using std::atomic_flag
1 #include <iostream>
2 #include <thread>
3 #include <atomic>
4
5 class SpinLockMutex {
6 public:
7 SpinLockMutex() {f_.clear();}
8 void lock() {
9 while (f_.test_and_set(std::memory_order_acquire)) {}
10 }
11 void unlock() {f_.clear(std::memory_order_release);}
12 private:
13 std::atomic_flag f_; // true if thread holds mutex
14 };
15
16 SpinLockMutex m;
17 unsigned long long counter = 0;
18
19 void doWork() {
20 for (unsigned long long i = 0; i < 100000ULL; ++i)
21 {m.lock(); ++counter; m.unlock();}
22 }
23
24 int main() {
25 std::thread t1(doWork), t2(doWork);
26 t1.join(); t2.join();
27 std::cout << counter << ’\n’;
28 }
■ uses acquire-release model
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1196
Example: Spinlock Mutex and std::scoped_lock
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() {
10 while (f_.test_and_set(std::memory_order_acquire)) {}
11 }
12 void unlock() {f_.clear(std::memory_order_release);}
13 private:
14 std::atomic_flag f_; // true if thread holds mutex
15 };
16
17 SpinLockMutex m;
18 unsigned long long counter = 0;
19
20 void doWork() {
21 for (unsigned long long i = 0; i < 100000ULL; ++i)
22 {std::scoped_lock lg(m); ++counter;}
23 }
24
25 int main() {
26 std::thread t1(doWork), t2(doWork);
27 t1.join(); t2.join();
28 std::cout << counter << ’\n’;
29 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1197
Carries-A-Dependency Relationships
■ For two operations A and B performed in the same thread, A is said to
carry a dependency to B if the result of A is used as an operand for B
(ignoring some special cases). ⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 4.7.1/7]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1199
Inter-Thread Happens-Before Relationships Revisited
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1200
Consume-Release Model
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1201
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–2019 Michael D. Adams C++ Version: 2019-02-04 1202
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–2019 Michael D. Adams C++ Version: 2019-02-04 1203
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–2019 Michael D. Adams C++ Version: 2019-02-04 1204
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):
2 if current position not set, return any element in sequence and set current
position to that of returned element
2 otherwise, either leave current position unchanged or move later in
sequence and return value at current position
■ write operation (e.g., store):
2 append value to end of sequence
2 set current position to correspond to appended value
■ read-modify-write operation (e.g., increment, decrement, exchange,
compare_exchange):
2 read last value from sequence
2 modify read value as appropriate to obtain new value
2 append new value to end of sequence
2 set current position to correspond to that of appended value
■ considerable flexibility in value returned by read
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1205
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–2019 Michael D. Adams C++ Version: 2019-02-04 1206
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–2019 Michael D. Adams C++ Version: 2019-02-04 1207
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–2019 Michael D. Adams C++ Version: 2019-02-04 1208
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–2019 Michael D. Adams C++ Version: 2019-02-04 1209
Release Semantics for Memory Operations
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1210
Acquire Semantics for Memory Operations
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1211
Release Sequences
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1212
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–2019 Michael D. Adams C++ Version: 2019-02-04 1213
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–2019 Michael D. Adams C++ Version: 2019-02-04 1214
std::atomic_thread_fence
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1215
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. [C++17 32.9/2]
⁓⁓⁓⁓⁓⁓⁓
■ 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. [C++17 32.9/3]
⁓⁓⁓⁓⁓⁓⁓
■ 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. [C++17 32.9/4]
⁓⁓⁓⁓⁓⁓⁓
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1216
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–2019 Michael D. Adams C++ Version: 2019-02-04 1217
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–2019 Michael D. Adams C++ Version: 2019-02-04 1218
Memory Orders: The Bottom Line
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1219
Section 3.9.11
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1220
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1221
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–2019 Michael D. Adams C++ Version: 2019-02-04 1222
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1223
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–2019 Michael D. Adams C++ Version: 2019-02-04 1224
Talks III
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1225
Section 3.10
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1226
Application Binary Interfaces (ABIs)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1227
Name Mangling
■ name mangling is process that maps set of names to another set of
names
■ typically name mangling used to map names in source code (which may
be overloaded) to names in object code
■ to keep linkers from becoming overly complex, do not want linkers to have
to support overloaded identifiers
■ thus, each overloaded function must have distinct name in object code
■ unique names in object code obtained by applying name mangling to
names from source code
■ name mangling rules not specified by C++ standard
■ typically, name mangling rules addressed by ABI standard, such as
Itanium ABI
■ some examples of name mangling results with Itanium ABI as follows:
Declaration Mangled Name
bool func(int) _Z4funci
bool func(double, double) _Z4funcdd
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1228
Section 3.10.1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1229
Itanium C++ ABI
■ one ABI followed by number of popular C++ compilers is Itanium C++ ABI
■ Itanium C++ ABI builds on various other ABI documents (some of which
are related to either System V or Intel Itanium architecture)
■ amongst other things, Itanium C++ ABI specifies:
2 some implementation details for constructors and destructors
2 name mangling rules
■ web site:
2 https://itanium-cxx-abi.github.io/cxx-abi/abi.html
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1230
Constructors
■ each constructor written by programmer nominally results in compiler
emitting code for multiple functions, which together implement several
variations on construction
■ each constructor for class T nominally associated with three functions:
1 base object constructor: function that creates non-virtual (direct) base
class objects and non-static data members of T and initializes T object itself
2 complete object constructor: function that creates all virtual base class
object constructor after obtaining storage for new T object from allocation
function (i.e., operator new)
■ if no virtual base classes, complete object constructor and base object
constructor are same (e.g., one function is alias for other)
■ ABI specification seems to suggest that allocating constructor only
required if class has virtual destructor
■ this said, however, GCC appears to never emit code for allocating object
constructor
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1231
Destructors
object itself and destroys non-static data members and non-virtual (direct)
base class objects of T
2 complete object destructor: function that, in addition to actions of base
object destructor, destroys all virtual base class objects of T
3 deleting destructor: function that, in addition to actions of complete object
destructor, invokes appropriate deallocation function for T
■ complete object destructor in charge of destroying virtual base class
objects, whereas base object destructor is not
■ if no virtual base classes, base object destructor and complete object
destructor are same (e.g., one is alias for other)
■ deleting destructor must be emitted when T has virtual destructor;
otherwise, may be emitted but not required
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1232
Name Mangling
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1233
Name Mangling (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1234
Name Mangling Examples
1 #include <iostream>
2 #include <vector>
3
4 struct Widget {
5 Widget();
6 Widget(int);
7 ˜Widget();
8 int grog() const;
9 };
10 Widget::Widget() {}
11 // Widget::Widget() -> _ZN6WidgetC1Ev, _ZN6WidgetC2Ev
12 Widget::Widget(int i) {}
13 // Widget::Widget(int) -> _ZN6WidgetC1Ei, _ZN6WidgetC2Ei
14 Widget::˜Widget() {}
15 // Widget::˜Widget() -> _ZN6WidgetD1Ev, _ZN6WidgetD2Ev
16 int Widget::grog() const {return 42;}
17 // Widget::grog() const -> _ZNK6Widget4grogEv
18
19 namespace foo {
20 void func() {}
21 // foo::func() -> _ZN3foo4funcEv
22 }
23
24 void squander(const std::vector<int>& v) {}
25 // squander(const std::vector<int>& v) ->
26 // _Z8squanderRKSt6vectorIiSaIiEE
27
28 int main() {
29 std::cout << "Hello, World!\n"; // std::cout -> _ZSt4cout
30 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1235
Name-Demangling Tools
■ C++Filt:
2 c++filt program, which is part of GNU Binary Utilities
2 filter that copies character stream from standard input to standard output
replacing any mangled names with their unmangled forms
2 web site: https://www.gnu.org/software/binutils
■ LLVM Cxxfilt:
2 llvm-cxxfilt program, which is part of LLVM software
2 filter that copies character stream from standard input to standard output
replacing any mangled names with their unmangled forms (in similar
fashion as c++filt)
2 LLVM web site: https://llvm.org
■ online name demangler:
2 http://demangler.com
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1236
Section 3.10.2
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1237
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1238
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1239
Part 4
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1240
Section 4.1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1241
Undefined, Unspecified, and Implementation-Defined
Behavior
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1243
Examples of Undefined Behavior (Continued)
■ performing pointer arithmetic that yields a result before start of or after
end (i.e., one past last element) of an array; for example:
int v[10];
int* p = &v[0];
--p; // undefined behavior
■ using pointers to objects whose lifetime has ended
■ left-shifting values by a negative amount; for example:
int i = 1;
i <<= (-3); // undefined behavior
■ shifting values by an amount greater than or equal to the number of bits in
the number; for example:
int i = 42;
i >>= 10000; // undefined behavior
■ using an automatic variable whose value has not been initialized; for
example:
void func() {
int i;
++i; // undefined behavior
}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1244
Examples of Unspecified Behavior
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1245
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 ⁓⁓⁓⁓⁓⁓⁓⁓⁓⁓
[C++17 26.3.11.1/2])
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1247
Section 4.2
C++ Compatibility
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1248
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 incompatibilities 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–2019 Michael D. Adams C++ Version: 2019-02-04 1249
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1250
Talks II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1251
Section 4.3
C Compatibility
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1252
C Compatibility
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1253
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–2019 Michael D. Adams C++ Version: 2019-02-04 1254
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–2019 Michael D. Adams C++ Version: 2019-02-04 1255
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–2019 Michael D. Adams C++ Version: 2019-02-04 1256
More Restrictive Conversions Involving void*
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1257
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–2019 Michael D. Adams C++ Version: 2019-02-04 1258
Type of Character Literal
1 #include <stdio.h>
2
3 int main() {
4 printf("%d\n", sizeof(’a’));
5 }
■ A character literal (such as ’A’) is of type char in C++, but type int in C.
■ Consequently, the above program will print a value of 1 when compiled
as C++ and a value greater than 1 (namely, the value of sizeof(int))
when compiled as C.
■ Thus, the same source code can have different semantics, depending on
whether it is interpreted as C++ or C.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1259
Part 5
Libraries
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1260
Section 5.1
Boost Libraries
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1261
Section 5.1.1
Introduction
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1262
Boost Libraries
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1263
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–2019 Michael D. Adams C++ Version: 2019-02-04 1264
Some Boost Libraries (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1265
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–2019 Michael D. Adams C++ Version: 2019-02-04 1266
Some Boost Libraries (Continued 3)
Concurrent Programming
Library Description
Fiber userland threads library
Compute parallel/GPU computing library
Lockfree lock-free containers (e.g., stacks and queues)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1267
Section 5.1.2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1268
Boost Container Library
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1269
Container Types
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1270
Container Types (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1271
flat_set Example
1 #include <iostream>
2 #include <cassert>
3 #include <boost/container/flat_set.hpp>
4
5 int main() {
6 namespace bc = boost::container;
7 bc::flat_set<std::string> c;
8 c.reserve(4);
9 c.insert("hi");
10 c.insert("apple");
11 c.insert("bye");
12 c.insert("foo");
13 for (auto&& i : c) {
14 std::cout << i << ’\n’;
15 }
16 std::cout << ’\n’;
17 auto j = c.find("foo");
18 assert(j != c.end() && *j == "foo");
19 c.erase(j);
20 c.shrink_to_fit();
21 for (auto&& i : c) {
22 std::cout << i << ’\n’;
23 }
24 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1272
Section 5.1.3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1273
Boost Intrusive Library
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1274
Container Types
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1275
Container Types (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1276
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–2019 Michael D. Adams C++ Version: 2019-02-04 1277
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–2019 Michael D. Adams C++ Version: 2019-02-04 1278
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–2019 Michael D. Adams C++ Version: 2019-02-04 1279
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–2019 Michael D. Adams C++ Version: 2019-02-04 1280
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–2019 Michael D. Adams C++ Version: 2019-02-04 1281
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–2019 Michael D. Adams C++ Version: 2019-02-04 1282
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–2019 Michael D. Adams C++ Version: 2019-02-04 1283
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–2019 Michael D. Adams C++ Version: 2019-02-04 1284
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–2019 Michael D. Adams C++ Version: 2019-02-04 1285
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–2019 Michael D. Adams C++ Version: 2019-02-04 1286
Section 5.1.4
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1287
Boost Iterator Library
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1288
Forward Iterator Example: Iterator Class Without Boost (1)
1 #include <type_traits>
2 #include <iterator>
3 #include <cstddef>
4
5 // singly-linked list node base (for intrusive container)
6 template <class T> struct slist_node_base {
7 slist_node_base(T* next_) : next(next_) {}
8 T* next; // pointer to next node in list
9 };
10
11 // single-linked list iterator (const and non-const)
12 template <class T> class slist_iter {
13 public:
14 using iterator_category = std::forward_iterator_tag;
15 using value_type = typename std::remove_const_t<T>;
16 using difference_type = std::ptrdiff_t;
17 using reference = T&;
18 using pointer = T*;
19 slist_iter(T* node = nullptr) : node_(node) {}
20 template <class OtherT, class =
21 std::enable_if_t<std::is_convertible_v<OtherT*, T*>>>
22 slist_iter(const slist_iter<OtherT>& other) : node_(other.node_) {}
23 reference operator*() {return *node_;}
24 pointer operator->() {return node_;}
[see SFINAE]
.........
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1289
Forward Iterator Example: Iterator Class Without Boost (2)
25 slist_iter& operator++() {
26 node_ = node_->next;
27 return *this;
28 }
29 slist_iter operator++(int) {
30 slist_iter old(*this);
31 node_ = node_->next;
32 return old;
33 }
34 template <class OtherT> bool operator==(const slist_iter<OtherT>& other)
35 const {return node_ == other.node_;}
36 template <class OtherT> bool operator!=(const slist_iter<OtherT>& other)
37 const {return !(*this == other);}
38 private:
39 template <class> friend class slist_iter;
40 T* node_; // pointer to list node
41 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1290
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–2019 Michael D. Adams C++ Version: 2019-02-04 1291
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–2019 Michael D. Adams C++ Version: 2019-02-04 1292
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–2019 Michael D. Adams C++ Version: 2019-02-04 1293
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–2019 Michael D. Adams C++ Version: 2019-02-04 1294
Random-Access Iterator Example: Iterator Class Without Boost (3)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1295
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–2019 Michael D. Adams C++ Version: 2019-02-04 1296
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–2019 Michael D. Adams C++ Version: 2019-02-04 1297
Section 5.1.5
Miscellaneous Examples
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1298
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–2019 Michael D. Adams C++ Version: 2019-02-04 1299
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–2019 Michael D. Adams C++ Version: 2019-02-04 1300
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–2019 Michael D. Adams C++ Version: 2019-02-04 1301
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–2019 Michael D. Adams C++ Version: 2019-02-04 1302
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–2019 Michael D. Adams C++ Version: 2019-02-04 1303
2-D Array Class With multi_array (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1304
multi_array Array Layout Example
1 #include <algorithm>
2 #include <iostream>
3 #include <boost/multi_array.hpp>
4
5 template <class T> std::ostream& operator<<(std::ostream& out,
6 const boost::multi_array_ref<T, 2>& x) {
7 const int m = x.shape()[0];
8 const int n = x.shape()[1];
9 for (int i = 0; i < m; ++i) {
10 for (int j = 0; j < n; ++j)
11 {out << ((j != 0) ? " " : "") << x[i][j];}
12 out << ’\n’;
13 }
14 return out;
15 }
16
17 int main() {
18 constexpr int n = 3;
19 float data[n * n] = {
20 1, 2, 3,
21 4, 5, 6,
22 7, 8, 9
23 };
24 int row_major[] = {1, 0};
25 bool ascending[] = {false, true};
26 // use column-major order
27 boost::multi_array_ref<float, 2> x(data, boost::extents[n][n],
28 boost::general_storage_order<2>(boost::fortran_storage_order()));
29 // use row-major order but with rows in reverse order
30 boost::multi_array_ref<float, 2> y(data, boost::extents[n][n],
31 boost::general_storage_order<2>(row_major, ascending));
32 // use row-major order
33 boost::multi_array<float, 2> z(boost::extents[n][n]);
34 std::copy_n(data, n * n, z.data());
35 std::cout << x << ’\n’ << y << ’\n’ << z << ’\n’;
36 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1305
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–2019 Michael D. Adams C++ Version: 2019-02-04 1306
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–2019 Michael D. Adams C++ Version: 2019-02-04 1307
Section 5.1.6
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1308
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1309
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1310
Section 5.2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1311
Computational Geometry Algorithms Library (CGAL)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1313
Handles
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1314
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:
2 has well defined first and last element
2 fits well with iterator model
■ circular sequence:
2 does not have well defined first and last element
2 does not fit well with iterator model
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1315
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:
2 type to allow iteration over all halfedges incident on vertex in polygon mesh
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1316
Section 5.2.1
Geometry Kernels
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1317
Real Number Types
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1318
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 extractor (i.e., operator>>) for MP_Float first reads double
and then converts to MP_Float
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1319
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–2019 Michael D. Adams C++ Version: 2019-02-04 1320
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–2019 Michael D. Adams C++ Version: 2019-02-04 1321
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–2019 Michael D. Adams C++ Version: 2019-02-04 1322
Point Representation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1323
Simple_cartesian and Cartesian Classes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1324
Simple_homogeneous and Homogeneous Classes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1325
Constructions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1326
Predicates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1327
Kernel Member Types: Basic Types
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1328
Kernel Member Types: Geometric Objects in Two Dimensions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1329
Kernel Member Types: Geometric Objects in Three Dimensions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1330
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–2019 Michael D. Adams C++ Version: 2019-02-04 1331
Filtered_kernel Class
■ class to convert kernel with inexact predicates into one with exact
predicates
■ declared as:
template <class K> Filtered_kernel<K>
■ K is kernel from which to make filtered kernel
■ predicates of K replaced by predicates using numeric type Interval_nt
■ if interval arithmetic can yield reliable answer, result used
■ otherwise, exception thrown and caught by class and predicate using
MP_Float used
■ for exact predicates with Simple_cartesian<double>, use:
Filtered_kernel<Simple_cartesian<double>> or equivalently
Exact_predicates_inexact_constructions_kernel
■ Exact_predicates_inexact_constructions_kernel very commonly
used
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1332
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–2019 Michael D. Adams C++ Version: 2019-02-04 1333
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–2019 Michael D. Adams C++ Version: 2019-02-04 1334
Execution of Filtered Predicate
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1335
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–2019 Michael D. Adams C++ Version: 2019-02-04 1336
Filtered Predicate Example (Continued)
Polygon Meshes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1338
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–2019 Michael D. Adams C++ Version: 2019-02-04 1339
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–2019 Michael D. Adams C++ Version: 2019-02-04 1340
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–2019 Michael D. Adams C++ Version: 2019-02-04 1341
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–2019 Michael D. Adams C++ Version: 2019-02-04 1342
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–2019 Michael D. Adams C++ Version: 2019-02-04 1343
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–2019 Michael D. Adams C++ Version: 2019-02-04 1344
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–2019 Michael D. Adams C++ Version: 2019-02-04 1345
Polyhedron_3::Facet
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1346
Facet Function Members
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1347
Polyhedron_3::Vertex
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1348
Vertex Function Members
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1349
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–2019 Michael D. Adams C++ Version: 2019-02-04 1350
Polyhedron_3::Halfedge (Continued)
■ halfedge contains:
2 handle for next halfedge around incident facet in CCW direction
2 handle for opposite halfedge
■ together, these two handles allow for efficient iteration around:
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1352
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–2019 Michael D. Adams C++ Version: 2019-02-04 1353
Halfedge Function Members (Continued 2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1354
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–2019 Michael D. Adams C++ Version: 2019-02-04 1355
Polyhedron_3 I/O
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1356
Polyhedron_3 Gotchas
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1357
Section 5.2.3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1358
Subdivision Methods
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1359
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–2019 Michael D. Adams C++ Version: 2019-02-04 1361
Mesh Generation Program: meshMake
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1362
Mesh Information Program: meshInfo
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1363
Mesh Subdivision Program: meshSubdivide
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1364
Section 5.3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1365
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–2019 Michael D. Adams C++ Version: 2019-02-04 1366
Event-Driven Model
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1367
Structure of GLUT Application
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1368
GLUT Header Files
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1369
Event Types
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1370
Event Types (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1371
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–2019 Michael D. Adams C++ Version: 2019-02-04 1372
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–2019 Michael D. Adams C++ Version: 2019-02-04 1373
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–2019 Michael D. Adams C++ Version: 2019-02-04 1374
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–2019 Michael D. Adams C++ Version: 2019-02-04 1375
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–2019 Michael D. Adams C++ Version: 2019-02-04 1376
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–2019 Michael D. Adams C++ Version: 2019-02-04 1377
Minimalist GLUT Program
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1378
Minimalist GLUT Program: Source Code
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1379
GLUT References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1380
Section 5.4
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1381
OpenGL Framework (GLFW) Library
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1382
GLFW Versus GLUT
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1383
Event-Driven Model
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1384
Structure of GLFW Application
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1385
GLFW Header Files
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1386
Event Types
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1387
Event Types (Continued 1)
Other Events
Event Type Description
error error has occurred in GLFW library
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1388
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–2019 Michael D. Adams C++ Version: 2019-02-04 1389
Functions (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1390
Functions (Continued 2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1391
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–2019 Michael D. Adams C++ Version: 2019-02-04 1392
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–2019 Michael D. Adams C++ Version: 2019-02-04 1393
Functions (Continued 5)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1394
Functions (Continued 6)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1395
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–2019 Michael D. Adams C++ Version: 2019-02-04 1396
Functions (Continued 8)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1397
Functions (Continued 9)
Clipboard
Function Description
glfwGetClipboardString gets contents of clipboard as string
glfwSetClipboardString sets clipboard to specified string
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1398
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–2019 Michael D. Adams C++ Version: 2019-02-04 1399
Functions (Continued 11)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1400
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–2019 Michael D. Adams C++ Version: 2019-02-04 1401
Minimalist GLFW Program
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1402
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–2019 Michael D. Adams C++ Version: 2019-02-04 1403
GLFW References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1404
Section 5.5
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1405
OpenGL Mathematics (GLM) Library
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1406
GLM Header Files
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1407
Types
■ matrix types:
2 mat2x2, mat2x3, mat2x4, mat2,
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1408
Functions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1409
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–2019 Michael D. Adams C++ Version: 2019-02-04 1410
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–2019 Michael D. Adams C++ Version: 2019-02-04 1411
Section 5.6
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1412
Open Graphics Library (OpenGL)
CPU GPU
Memory Memory
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1415
OpenGL State Machine
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1416
Contexts and Profiles
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1418
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–2019 Michael D. Adams C++ Version: 2019-02-04 1419
Function Naming Conventions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1421
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–2019 Michael D. Adams C++ Version: 2019-02-04 1422
Provoking Vertex
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1423
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–2019 Michael D. Adams C++ Version: 2019-02-04 1425
Vertex Buffer Objects (VBOs)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1426
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–2019 Michael D. Adams C++ Version: 2019-02-04 1428
Transformations
Normalized
Device Window
Clipping and Coordinates Viewport Coordinates
Perspective Transformation
Division
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1429
Transformations (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1430
Depth Buffering
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1431
Face Culling
v2 v2
v0 v1 v1 v0
Counterclockwise (CCW) Clockwise (CW)
Winding Order Winding Order
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1432
State Management
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1433
Other Functions
Function Description
glClear clear buffer to preset values
glClearColor specify clear values for color buffers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1434
Program Structure
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1435
Section 5.6.1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1436
OpenGL Application Program Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1437
Header Files
1 #include <cstdlib>
2 #include <string>
3 #include <GL/glew.h>
4 #include <GLFW/glfw3.h>
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1438
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–2019 Michael D. Adams C++ Version: 2019-02-04 1439
Make Window
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1440
Vertex and Fragment Shaders
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1441
Compiling Shaders
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1442
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–2019 Michael D. Adams C++ Version: 2019-02-04 1443
Initialize Vertex Array Object (VAO)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1444
Window Refresh Callback
6 GLuint vao = 0;
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1445
Section 5.6.2
Shaders
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1446
Shaders
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1447
Rendering Pipeline and Shaders
GPU
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1448
OpenGL Shader Language (GLSL)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1449
Reserved Keywords
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1450
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–2019 Michael D. Adams C++ Version: 2019-02-04 1451
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–2019 Michael D. Adams C++ Version: 2019-02-04 1452
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–2019 Michael D. Adams C++ Version: 2019-02-04 1453
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–2019 Michael D. Adams C++ Version: 2019-02-04 1454
Basic Types (Continued 2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1455
Operators
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1456
Operators (Continued 1)
■ first, second, third, and fourth components of vector (if they exist) can be
selected by:
2 subscripting operator with subscripts 0, 1, 2, and 3, respectively; or
2 selection operator with x, y, z, and w, respectively; or
2 selection operator with r, g, b, and a, respectively; or
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1457
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–2019 Michael D. Adams C++ Version: 2019-02-04 1458
Control Flow
■ selection statements
2 if
2 if-else
2 ternary operator
2 switch
■ looping statements
2 for
2 while
2 do-while
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1459
Functions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1460
Constructors
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1461
Conversions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1463
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–2019 Michael D. Adams C++ Version: 2019-02-04 1464
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–2019 Michael D. Adams C++ Version: 2019-02-04 1465
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–2019 Michael D. Adams C++ Version: 2019-02-04 1466
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–2019 Michael D. Adams C++ Version: 2019-02-04 1467
Built-In Functions (Continued 5)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1468
The in and out Qualifiers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1469
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–2019 Michael D. Adams C++ Version: 2019-02-04 1470
The uniform Qualifier
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1472
Interpolation Example
■ single triangle rendered with vertices having color attributes of red, green,
and blue, with provoking vertex being last vertex
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1473
Layout Qualifiers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1474
Configuration with Vertex and Fragment Shaders
Application Program
Shader Program
Uniform
Variables
Vertex Shader Fragment Shader
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1475
Various Configurations of Shaders
Vertex Fragment
Shader Primitive Shader
Assembly
and
Rasterization
Vertex and Fragment Shaders
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1476
Vertex Shaders
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1477
Vertex Shader Inputs and Outputs
instanced rendering
■ other inputs associated with vertex attributes from VAO/VBO
■ built-in output variables:
2 vec4 gl_Position: clip-space output position of current vertex
2 float gl_PointSize: pixel width/height of point being rasterized; only
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1478
Vertex Shader Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1479
Fragment Shaders
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1480
Fragment Shader Inputs and Outputs
gl_FragCoord.z
■ vec4 output variable for fragment color
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1481
Fragment Shader Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1482
Geometry Shaders
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1483
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:
2 vec4 gl_Position: vertex position
2 float gl_PointSize: pixel width/height of point being rasterized; only
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1484
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:
2 vec4 gl_Position: vertex position
2 float gl_PointSize: pixel width/height of point being rasterized; only
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1486
Using Shader Programs
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1488
Identifying Shader Variables in Application (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1489
Associating Data in VAO with Attribute Variable
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1490
Example: Associating Data in VAO with Attribute Variable
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1491
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–2019 Michael D. Adams C++ Version: 2019-02-04 1492
Section 5.6.3
Shader Examples
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1493
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–2019 Michael D. Adams C++ Version: 2019-02-04 1494
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–2019 Michael D. Adams C++ Version: 2019-02-04 1495
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–2019 Michael D. Adams C++ Version: 2019-02-04 1496
Mandelbrot: Background
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1497
Mandelbrot: Application Program
v3 = (−1, 1) v2 = (1, 1)
t3 = (− 12 , 12 ) t2 = ( 12 , 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 [− 12 , 21 ] × [− 12 , 12 ] corresponds to full viewport
■ square region in complex plane of width/height scale centered at point
center is mapped onto region [− 12 , 21 ] × [− 12 , 21 ] in texture coordinates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1498
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–2019 Michael D. Adams C++ Version: 2019-02-04 1499
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–2019 Michael D. Adams C++ Version: 2019-02-04 1500
ShrinkFace: Shader Example
v1
v′1
c
v′2 v′0
v2 v0
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1502
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–2019 Michael D. Adams C++ Version: 2019-02-04 1503
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–2019 Michael D. Adams C++ Version: 2019-02-04 1504
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–2019 Michael D. Adams C++ Version: 2019-02-04 1505
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–2019 Michael D. Adams C++ Version: 2019-02-04 1506
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–2019 Michael D. Adams C++ Version: 2019-02-04 1507
Wireframe: Shader Example
1 1
2 p0 + 0p1 + 2 p2 0p0 + 12 p1 + 12 p2
≡ ( 21 , 0, 12 ) ≡ (0, 12 , 12 )
1 1 1
3 p0 + 3 p1 + 3 p2
≡ ( 31 , 13 , 13 )
1 1
p0 2 p0 + 2 p1 + 0p2 p1
1p0 + 0p1 + 0p2 ≡ ( 21 , 21 , 0) 0p0 + 1p1 + 0p2
≡ (1, 0, 0) ≡ (0, 1, 0)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1509
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–2019 Michael D. Adams C++ Version: 2019-02-04 1510
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–2019 Michael D. Adams C++ Version: 2019-02-04 1511
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 }
■ vectors:
2 ℓ: unit vector vector in direction from point on surface to light source
2 n: unit normal at point on surface
2 v: unit vector in direction from point on surface to viewer
2 r : unit vector in direction that perfectly reflected light ray would take from
Light Source
n
r
Eye
v θ θ −ℓ
Surface
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1514
Per-Vertex Lighting: Shader Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1515
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–2019 Michael D. Adams C++ Version: 2019-02-04 1516
Per-Vertex Lighting: Vertex Shader (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1517
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–2019 Michael D. Adams C++ Version: 2019-02-04 1518
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–2019 Michael D. Adams C++ Version: 2019-02-04 1520
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–2019 Michael D. Adams C++ Version: 2019-02-04 1521
Per-Fragment Lighting: Fragment Shader (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1522
Section 5.6.4
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1523
OpenGL Example Program: simple_2d
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1524
OpenGL Example Program: simple_3d
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1525
OpenGL Example Program: cube
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1526
OpenGL/CGAL Example Program: wireframe
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1527
Section 5.6.5
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1528
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1529
References II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1530
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1531
Software
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1532
Section 5.7
Other Libraries
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1533
Numerical Libraries I
■ Eigen
2 C++ library for linear algebra
2 web site: http://eigen.tuxfamily.org
■ Lapack++
2 C++ library for high-performance linear-algebra computations
2 C++ wrapper for LAPACK and BLAS
2 web site: http://lapackpp.sourceforge.net
■ Armadillo
2 C++ library for linear algebra
2 web site: http://arma.sourceforge.net
■ GNU Scientific Library
2 C library for numerical analysis
2 web site: http://www.gnu.org/software/gsl
■ GNU Multiprecision Library
2 C library for arbitrary-precision arithmetic
2 web site: http://gmplib.org
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1534
Numerical Libraries II
■ Boost.uBLAS
2 C++ library for numerical computation
2 web site:
http://www.boost.org/doc/libs/release/libs/numeric/ublas
■ Boost.Rational
2 C++ rational number library
2 web site: www.boost.org/doc/libs/release/libs/rational
■ Boost.Interval
2 C++ interval arithmetic library
2 web site: www.boost.org/doc/libs/release/libs/numeric/
interval/doc/interval.htm
■ Boost.Math
2 C++ library
2 provides math constants, GCD, LCM, quaternions, and more
2 web site: http://www.boost.org/doc/libs/release/libs/math
■ Linear Algebra Package (LAPACK)
2 Fortran library for numerical computing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1535
Numerical Libraries III
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1536
Part 6
Programming
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1537
Section 6.1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1538
Formatting, Naming, Documenting
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1539
Error Handling
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1540
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–2019 Michael D. Adams C++ Version: 2019-02-04 1541
Code Duplication
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1542
Miscellany
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1543
Miscellany
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1544
Testing: Preconditions and Postconditions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1545
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–2019 Michael D. Adams C++ Version: 2019-02-04 1546
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–2019 Michael D. Adams C++ Version: 2019-02-04 1547
Section 6.2
Algorithms
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1548
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:
2 CPU instruction count
2 cache efficiency
2 degree of parallelism and concurrency
2 resource utilization (e.g., memory, disk, and network)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1549
Random-Access Machine (RAM) Model
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1552
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:
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1554
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:
2 f (n) = 3n2 + 2n + 1; f ∈ O(n2 ) and f ∈ O(n3 ) but f ̸∈ O(n)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1555
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–2019 Michael D. Adams C++ Version: 2019-02-04 1556
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:
2 f (n) = 5n3 + n; f ∈ Ω(n3 ) and f ∈ Ω(n2 ) but f ̸∈ Ω(n4 )
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1558
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:
2 f (n) = 3n3 + 2n + 1; f ∈ o(n5 ) and f ∈ o(n4 ) but f ̸∈ o(n3 )
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1559
Small-Omega (ω) Notation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1560
Asymptotic Notation in Equations and Inequalities
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1561
Properties of Θ, O, and Ω
■ sum of functions:
2 if f ∈ Θ(g) and f ∈ Θ(g), then f + f ∈ Θ(g)
1 2 1 2
2 if f ∈ O(g) and f ∈ O(g), then f + f ∈ O(g)
1 2 1 2
2 if f 1 ∈ Ω(g) and f 2 ∈ Ω(g), then f 1 + f 2 ∈ Ω(g)
■ multiplication by constant:
2 for all positive functions f and all positive constants a, a f ∈ Θ( f ),
a f ∈ O( f ), and a f ∈ Ω( f )
■ product of functions:
2 for all positive functions f1 , f2 , g1 , g2 , if f1 ∈ Θ(g1 ) and f2 ∈ Θ(g2 ), then
f1 f2 ∈ Θ(g1 g2 )
2 for all positive functions f1 , f2 , g1 , g2 , if f1 ∈ O(g1 ) and f2 ∈ O(g2 ), then
f1 f2 ∈ O(g1 g2 )
2 for all positive functions f1 , f2 , g1 , g2 , if f1 ∈ Ω(g1 ) and f2 ∈ Ω(g2 ), then
f1 f2 ∈ Ω(g1 g2 )
■ examples:
2 if f ∈ Θ(n), then n f (n) ∈ Θ(n2 )
2 if f and g are positive functions in Θ(1), then f + g ∈ Θ(1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1562
Additional Remarks
■ log2 n ∈ Θ(logb n) for all b > 1 (i.e., base of logarithm does not impact
asymptotic analysis)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1563
Remarks on Asymptotic Complexity
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1564
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!)
n
double exponential O(ab )
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1565
Recurrence Relations
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1566
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:
L−1
f (n) = g(n) + ∑i=0 ai f (bi n + hi (n))
■ need to be careful about non-integer sequence indices arising in
recurrence relations like:
L−1
T (n) = ∑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–2019 Michael D. Adams C++ Version: 2019-02-04 1567
Solutions for Some Common Recurrence Relations
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1568
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–2019 Michael D. Adams C++ Version: 2019-02-04 1569
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 object of type T requires O(1) space, each of a, b, c, i, j, k,
and sum, requires Θ(1) space
■ asymptotic space complexity is Θ(1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1570
Iterative Fibonacci Algorithm: Time Complexity
■ consider iterative algorithm for computing nth Fibonacci number:
1 unsigned long long fibonacci(unsigned int n) {
2 unsigned long long a[3] = {1, 1, 1};
3 for (int i = 3; i <= n; ++i) {
4 a[0] = a[1];
5 a[1] = a[2];
6 a[2] = a[0] + a[1];
7 }
8 return a[2];
9 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1572
Recursive Fibonacci Algorithm: Time Complexity
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1573
Recursive Fibonacci Algorithm: Space Complexity
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1574
Amdahl’s Law
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1575
Section 6.2.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1576
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1578
Section 6.3
Data Structures
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1579
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:
2 general nature of entity represented by type
2 set of allowable states/values that type can assume
2 set of operations that can be performed on type
2 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:
2 can assume integer values
2 provides basic arithmetic operations, relational operations, and so on
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1580
Container ADTs
std::unordered_map, std::unordered_multimap
2 boost::intrusive::slist, boost::intrusive::list
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1582
Iterator ADTs
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1584
Container and Iterator Considerations (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1585
Section 6.3.1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1586
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:
2 clear: remove all elements from list
2 is empty: test if list empty
2 size: query number of elements in list
2 insert: insert element in list
2 remove: remove element from list
■ operations for traversing elements in list (which are often provided via
iterator ADT) include:
2 successor: get next element in list
2 predecessor (optional): get previous element in list
■ examples of realizations of list ADT:
2 std::vector, std::forward_list, and std::list
2 boost::intrusive::slist and boost::intrusive::list
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1587
Array-Based Lists
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1588
Array-Based Lists: Diagram
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1589
Remarks on Array-Based Lists
■ advantages:
2 elements stored contiguously in memory (which is cache friendly)
2 no per-element storage overhead
2 can insert at end of list in amortized O(1) time
2 can remove at end of list in O(1) time
2 can access element in any position in O(1) time
2 (random-access) iterator has storage cost of one pointer
■ disadvantages:
2 cannot insert or remove at start or arbitrary position in O(1) time
2 if capacity of array exceeded, memory reallocation and copying required
2 if array can be reallocated, insert at end can only at best guarantee
amortized (not worst-case) O(1) time
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1590
Singly-Linked Lists
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1591
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_; // pointer to list head pointer
19 // one of three possibilities:
20 // 1) iterator refers to before-begin position:
21 // head_ points to list head pointer and node_ is null
22 // 2) iterator refers to end position:
23 // head_ and node_ both null
24 // 3) iterator refers to element in list:
25 // head_ is null and node_ points to referenced node
26 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1592
Singly-Linked List: Diagram
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1593
Remarks on Singly-Linked Lists
■ advantages:
2 can insert element after (but not before) particular position in O(1) time
2 can remove element at start of list in O(1) time
2 no capacity exceeded problem like with array
2 reduced memory cost relative to doubly-linked list as consequence of node
not tracking predecessor
2 element references are stable
2 can find successor in list in O(1) time
■ disadvantages:
2 element data not contiguous in memory
2 has per-element storage overhead (1 pointer for successor)
2 cannot insert element before particular position in O(1) time
2 cannot remove element at arbitrary position in O(1) time
2 cannot efficiently iterate backwards over elements in list
2 cannot find predecessor in list in O(1) time
2 (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–2019 Michael D. Adams C++ Version: 2019-02-04 1594
Singly-Linked List With Header Node
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1595
Singly-Linked List With Header Node: Code
1 // list node base class
2 struct node_base {
3 // ...
4 node_base* next_;
5 };
6
7 // list node derived class (with list element)
8 template <class T> struct node : public node_base {
9 T elem_;
10 };
11
12 // list iterator class
13 template <class T> class slist_iter {
14 // ...
15 node_base* node_;
16 // one of three possibilities:
17 // 1) iterator refers to before-begin position:
18 // node_ points to header node
19 // 2) iterator refers to end position:
20 // node_ is null
21 // 3) iterator refers to element in list:
22 // node_ points to referenced element’s node
23 };
24
25 // list class
26 template <class T> class list {
27 // ...
28 node_base node_;
29 std::size_t size_;
30 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1596
Singly-Linked List With Header Node: Diagram
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1597
Remarks on Singly-Linked List With Header Node
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1598
Doubly-Linked Lists
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1599
Doubly-Linked Lists: Code
1 // list node class
2 template <class T> struct Node {
3 Node* next_; // pointer to next node in list
4 Node* prev_; // pointer to previous node in list
5 T elem_; // element
6 };
7
8 // iterator class
9 template <class T> class Iterator {
10 // ...
11 Node<T>* node_; // node of referenced element
12 Node<T>** tail_; // pointer to tail pointer of list
13 // tail_ always points to tail_ pointer in list object
14 // one of two possibilities:
15 // 1) iterator refers to end position:
16 // node_ is null
17 // 2) iterator refers to element in list:
18 // node_ points to referenced element’s node
19 };
20
21 // list class
22 template <class T> class List {
23 // ...
24 Node<T>* head__; // pointer to first node in list
25 Node<T>* tail_; // pointer to last node in list
26 std::size_t size_; // number of elements in list
27 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1600
Doubly-Linked List: Diagram
Iterator Iterator
(element) (end position)
node_ node_ 0
tail_ tail_
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1601
Remarks on Doubly-Linked Lists
■ advantages:
2 no capacity-exceeded problem like in array case
2 stable references to elements
2 can insert or remove at arbitrary position in O(1) time
2 can find successor and predecessor in O(1) time
2 can efficiently iterate both forwards and backwards over elements in list
■ disadvantages:
2 elements not stored contiguously in memory
2 per-element storage overhead (2 pointers)
2 relative to singly-linked list, has greater per-element storage overhead
(1 additional pointer for predecessor)
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1602
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 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–2019 Michael D. Adams C++ Version: 2019-02-04 1603
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 // one of two possibilities:
23 // 1) iterator refers to end position:
24 // node_ points to sentinel node
25 // 2) iterator refers to element in list:
26 // node_ points to referenced element’s node
27 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1604
Doubly-Linked List With Sentinel Node: Diagram
Iterator Iterator
(element) (end position)
node_ node_
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1605
Remarks on Doubly-Linked Lists With Sentinel Node
■ 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:
2 clear: remove all elements from stack
2 is empty: test if stack is empty
2 top: access element at top of stack (without removing)
2 push: add element to top of stack
2 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:
2 std::stack
2 boost::lockfree::stack
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1607
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–2019 Michael D. Adams C++ Version: 2019-02-04 1608
Array Implementation of Stack: Diagram
Stack Array of T
x0
start_
x1
end_ ..
.
ptr_ xn−1
..
.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1609
Remarks on Array Implementation of Stack
■ advantages:
2 elements stored contiguously in memory
2 no per-element storage overhead
■ disadvantages:
2 if capacity of array exceeded, must reallocate and copy
2 if array grown, can only guarantee amortized (not worst-case) O(1) time for
push
2 if array reallocated, elements references are invalidated
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1610
Node-Based Implementation of Stack
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1611
Node-Based Implementation of Stack: Diagram
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1612
Remarks on Node-Based Implementation of Stack
■ advantages:
2 no capacity-exceeded problem as in array case
2 can perform push operation in O(1) time in worst case
2 element references are stable
■ disadvantages:
2 element data not contiguous in memory
2 has per-element storage overhead (i.e., 1 pointer for successor)
2 relative to array-based implementation, requires more space
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1613
Queue ADT
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1614
Array Implementation of Queue
Queue Array of T
start_
end_ ..
.
head_
x0
tail_
x1
size_ n ..
.
xn−1
..
.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1616
Remarks on Array Implementation of Queue
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1617
Array of Arrays Implementation of Queue
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1618
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–2019 Michael D. Adams C++ Version: 2019-02-04 1619
Remarks on Array of Arrays Implementation of Queue
■ advantages:
2 elements never change their location so pointers and references to
elements are stable
■ disadvantages:
2 although each individual block holding element data is contiguous, blocks
not contiguous
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1620
Node-Based Implementation of Queue
Iterator
node_
tail_
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1622
Remarks on Node-Based Implementation of Queue
■ advantages:
2 enqueue and dequeue operations can be performed in O(1) time
2 stable element references
■ disadvantages:
2 elements not stored contiguously in memory
■ could use doubly-linked list with sentinel node in order to facilitate more
efficient iterator
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1623
Section 6.3.2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1624
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 2 A is root node
2 B is child of A
B C D
2 A is parent of B
E F G H 2 C and D are siblings of B
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1625
Tree Terminology
■ path of length k in tree is sequence of k + 1 nodes n0 , n1 , . . . , nk where ni
is parent of ni+1
■ node q said to be ancestor of node n if q is on path from root node to n
■ node q is said to be descendant of node n if q on path from n to leaf
■ every node is both ancestor and descendant of itself
■ node q said to be proper ancestor of n if ancestor of, and distinct from, n
■ node q is said to be proper descendant of n if q is descendant of, and
distinct from, n
■ example:
A 2 A, B, F is path of length 2
2 A and B are proper ancestors of E
B C D
2 E and F are proper descendants of B
E F G H 2 B is ancestor and descendant of B
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1626
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:
2 tree consisting of nodes B, E , and F
A is subtree associated with node B
2 degree of node B is 2
B C D
2 degree of tree is 3
■ example:
A
2 depths of nodes C and E are 1 and 2,
respectively
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1628
Tree Terminology (Continued 3)
■ example:
A
2 weights of nodes B and C are 2 and
B C D 0, respectively
2 weight of tree is 5
E F G H
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1629
Tree Traversal
■ example:
A
2 preorder traversal visits nodes in
order: A, B, E , F , C, D, G, H
B C D
2 postorder traversal visits nodes in
order: E , F , B, C, G, H , D, A
E F G H
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1630
Applications of Trees
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1631
Tree ADT
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1632
Node-Based Tree Implementation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1633
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–2019 Michael D. Adams C++ Version: 2019-02-04 1634
Binary Trees
■ example:
2 root node is A
A
2 left child of A is B
B C 2 right child of A is C
D E F
2 left subtree of A is tree consisting of nodes B, D, and E
2 right subtree of A is tree consisting of nodes C and F
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1635
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–2019 Michael D. Adams C++ Version: 2019-02-04 1636
Balanced Binary Trees
■ binary tree said to be perfectly balanced if left and right subtrees of each
(interior) node contain same number of nodes (i.e., perfect tree)
■ binary tree said to be strictly balanced if can be formed by discarding
zero or more leaf nodes from perfect tree
■ 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–2019 Michael D. Adams C++ Version: 2019-02-04 1637
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 2 preorder traversal order: A, B, D, E , C, F
2 postorder traversal order: D, E , B, F , C, A
B C
2 inorder traversal order: D, B, E , A, C, F
D E F
2 level order traversal order: A, B, C, D, E , F
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1638
Binary Tree ADT
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1639
Binary Tree ADT (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1640
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–2019 Michael D. Adams C++ Version: 2019-02-04 1642
Remarks on Node-Based Binary Tree
■ advantages:
2 can handle case of tree that is not complete without gross memory
inefficiency
2 can provide stable element references
■ disadvantages:
2 has per-element storage overhead (3 pointers: 1 for parent, 1 for first child,
and 1 for second child or next sibling)
2 element data not contiguous
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1643
Array-Based Binary Tree
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1644
Array-Based Binary Tree: Diagram
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1645
Remarks on Array-Based Binary Tree
■ advantages:
2 memory efficient: no per-element storage overhead (i.e., no memory cost
for representing connectivity of nodes in tree)
2 cache efficient: element data stored contiguously in memory
■ disadvantages:
2 can only handle complete trees
2 although could generalize this approach to handle non-complete tree,
would be grossly inefficient in terms of memory usage
2 if array capacity exceeded, costly reallocation and copy required
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1646
Binary Search Trees
■ binary tree is said to have binary search tree property if, for each node
node n with key k, following holds:
2 every key in left subtree of n is less than or equal to k; and
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1647
Heaps
■ tree said to have heap property if, for each node n in tree, following
holds:
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1648
Section 6.3.3
Hash Tables
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1649
Basic Idea Behind Hash Tables
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1650
Hash Tables
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1651
Hash Table Example
Index Slot
0000
0001 0019910001
0002 5919870002
0003
..
.
9997 1212009997
9998
9999 1122339999
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1652
Hash Functions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1653
Remarks on Hash-Code Maps
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1654
Remarks on Compression Maps
a mod m ̸= 0
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1655
Collision Resolution by Chaining
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1656
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–2019 Michael D. Adams C++ Version: 2019-02-04 1657
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
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1658
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–2019 Michael D. Adams C++ Version: 2019-02-04 1659
Quadratic Probing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1660
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–2019 Michael D. Adams C++ Version: 2019-02-04 1661
Double Hashing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1662
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–2019 Michael D. Adams C++ Version: 2019-02-04 1663
Random Hashing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1664
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:
2 1 for chaining
1
2
2 for open addressing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1666
Some Applications of Hash Tables
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1667
Section 6.3.4
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1668
Set and Multiset ADTs
boost::intrusive::unordered_multiset
2 boost::intrusive::set and boost::intrusive::multiset
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1669
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:
2 clear: remove all elements from map
2 is empty: test if map is empty
2 size: query number of elements in map
2 insert: insert element in map
2 remove: remove element from map
2 find: locate element in map if present based on its key
■ multimap ADT similar to map ADT except that keys need not be unique
■ example realizations of map/multimap ADT:
2 std::map and std::multimap
2 std::unordered_map and std::unordered_multimap
2 boost::intrusive::set and boost::intrusive::multiset
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1670
Remarks on Implementation of Sets and Maps
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1671
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:
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1673
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:
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1675
Treaps
boost::intrusive::treap_multiset
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1676
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:
2 boost::intrusive::splay_tree, boost::intrusive::splay_set,
and boost::intrusive::splay_multiset
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1677
Scapegoat Trees
boost::intrusive::sg_multiset
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1678
Section 6.3.5
Priority Queues
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1679
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:
2 front: access element at front of queue (i.e., element with highest priority)
2 insert: insert element in queue with specified priority
2 remove: remove element from front of queue (i.e.. element with highest
priority)
2 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:
2 std::priority_queue,
2 boost::heap::priority_queue and boost::heap::fibonacci_heap
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1680
Remarks on Priority Queue Implementations
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1681
Section 6.3.6
Graphs
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1682
Graphs
B C D B C D
E E
Undirected Directed
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1683
Graph ADTs
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1684
Adjacency Matrix Implementation of Graph
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1685
Adjacency List Implementation of Graph
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1686
Section 6.3.7
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1687
Naive Triangle-Mesh Data Structure
struct Face {
int vertexIndexes[3]; // indexes of vertices of triangle
};
Vertex vertices[numVertices]; // vertex array
Face triangles[numTriangles]; // triangle array
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1688
Naive Triangle-Mesh Data Structure Example
v2
= (−1, 1, −1)
f1
v3
f2 = (1, 1, 1)
f0
v0 v1
= (−1, −1, 1) = (1, −1, −1)
Vertices
Faces
Array Array
Array Array
Index Element
Index Element
0 (-1,-1,1)
0 0, 1, 3
1 (1,-1,-1)
1 1, 2, 3
2 (-1,1,-1)
2 0, 3, 2
3 (1,1,1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1689
Half-Edge Data Structure
■ described in:
K. Weiler. Edge-based data structures for solid modeling in
curved-surface environments.
IEEE Computer Graphics and Applications, 5(1):21–40, Jan. 1985.
■ every edge represented as pair of directed edges, each called half-edge
■ 6 pointers plus 2 bits (i.e., 2 one-bit integers) per edge
■ used in Computational Geometry Algorithms Library (CGAL)
■ representing edges in terms of directed line segments often
advantageous in algorithms
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1690
Half-Edge Data Structure (Continued)
e[0].next
e[0].left e[1].term
e[0]
e[1] vertex
e[0].term edge
e[1].left
face
e[1].next
struct HalfEdge {
int index; // index of half-edge in parent edge
HalfEdge* next; // next CCW half-edge around left face
Vertex* term; // terminal vertex
Face* left; // left face
};
struct Edge {
HalfEdge e[2]; // pair of symmetric half-edges
};
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1691
Quad-Edge Data Structure
■ proposed in:
L. Guibas and J. Stolfi. Primitives for the manipulation of general
subdivisions and the computation of Voronoi diagrams.
ACM Transactions on Graphics, 4(2):74–123, Apr. 1985.
■ simultaneously represents graph and its dual
■ each edge belongs to four circular singly-linked lists corresponding to two
vertices and two faces incident to edge
■ vertex/face represented by ring of quad-edges
■ 8 pointers plus 4 two-bit integers per edge
■ used in various research software available on Internet (e.g., Scape
terrain-simplification software, Dani Lischinski’s constrained DT software)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1692
Quad-Edge Data Structure (Continued)
e[1].data
e[1].next
e[2].next
e[1]
e[0].data
e[0]
vertex
e[2]
e[2].data e[3]
e[0].next
edge
e[3].next
e[3].data
face
struct QuadEdge {
int index; // index of quad-edge in parent edge
QuadEdge* next; // next CCW quad-edge with same origin
void* data; // face or vertex
};
struct Edge {
QuadEdge* e[4]; // four quad-edges of edge
};
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1693
Object File Format (OFF)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1694
OFF Example (Triangle Mesh)
v3 v2 OFF
= (−1, 1, 0) = (1, 1, 0) 5 4 0
-1 -1 0
1 -1 0
1 1 0
-1 1 0
0 0 1
v4 3 0 1 4
= (0, 0, 1) 3 1 2 4
3 2 3 4
v0 v1 3 0 4 3
= (−1, −1, 0) = (1, −1, 0)
Corresponding
Mesh
OFF File
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1695
OFF Example (Quad Mesh)
OFF
9 4 0
v6 v5 v4 -1 -1 -1
= (−1, 1, −1) = (0, 1, 0) = (1, 1, −1) 0 -1 0
1 -1 -1
1 0 0
1 1 -1
v7 v3 0 1 0
= (−1, 0, 0) v8 = (1, 0, 0) -1 1 -1
= (0, 0, 1) -1 0 0
0 0 1
4 0 1 8 7
v0 v1 v2 4 1 2 3 8
= (−1, −1, −1) = (0, −1, 0) = (1, −1, −1) 4 8 3 4 5
4 7 8 5 6
Mesh
Corresponding
OFF File
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1696
Section 6.3.8
Intrusive Containers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1697
Intrusive Containers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1698
Shortcomings of Non-Intrusive Containers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1699
Advantages of Intrusive Containers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1700
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:
2 changing key of element in map
2 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:
2 when destroying container before object, must be careful to avoid resource
leaks
2 destroying object while in container, likely to be disastrous since container
uses part of object to implement container
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1701
Disadvantages of Intrusive Containers (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1702
Intrusive Doubly-Linked List
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1703
Intrusive Doubly-Linked List: Code
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1704
Intrusive Doubly-Linked List: Code (Continued)
1 // list node with user data
2 struct Widget {
3 // ...
4 list_hook<Widget> hook; // public
5 // ...
6 };
7
8 // type for list of Widget objects
9 using Widget_list = list<Widget, &Widget::hook>;
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1705
Intrusive Doubly-Linked List: Diagram
.. .. ..
. . .
Iterator
node_
tail_
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1706
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–2019 Michael D. Adams C++ Version: 2019-02-04 1707
Intrusive Doubly-Linked List With Sentinel Node
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1708
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
14 // (constness of T determines if const_iterator)
15 template <class T, list_hook<T> T::* P> class list_iterator :
16 list_traits<T, P>{
17 // ...
18 list_hook* node_; // pointer to node of referenced element
19 };
20
21 // list
22 template <class T, list_hook<T> T::* P> class list :
23 list_traits<T, P> {
24 // ...
25 list_hook node_; // sentinel node
26 std::size_t size_; // number of elements in list
27 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1709
Intrusive Doubly-Linked List With Sentinel Node: Code (Continued)
1 // list node with user data
2 struct Widget {
3 // ...
4 list_hook hook; // public
5 // ...
6 };
7
8 // type for list of Widget objects
9 using Widget_list = list<Widget, &Widget::hook>;
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1710
Intrusive Doubly-Linked List With Sentinel Node: Diagram
Iterator
node_
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1711
Remarks on Intrusive Doubly-Linked List With Sentinel Node
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1712
Examples of Intrusive Containers
multiset/multimap)
2 boost::intrusive_ptr (intrusive reference-counted smart pointer)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1713
Section 6.3.9
Miscellany
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1714
Memory Management for Containers
■ for reasons of efficiency or functionality (or even correctness), often
necessary to:
2 separate memory allocation from construction
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1715
Row-Major Versus Column-Major Order
0 1 2 M−1 0 1 2 M−1
0 0
··· ···
1 1
··· ···
2 2
··· ···
.. .. .. . . .. .. .. .. . . ..
. . . . . . . . . .
··· ···
N −1 N −1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1716
Section 6.3.10
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1717
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1718
References II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1719
Section 6.4
Finite-Precision Arithmetic
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1720
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–2019 Michael D. Adams C++ Version: 2019-02-04 1721
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–2019 Michael D. Adams C++ Version: 2019-02-04 1722
Finite-Precision Number Representations
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1723
Fixed-Point Number Representations
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1725
Floating-Point Number Representations (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1726
IEEE 754 Standard (IEEE Std. 754-1985)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1728
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:
2 a sign bit, s
2 a biased exponent, e = E+ bias
■ 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 ̸= 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 ̸= 0 NaN
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1730
IEEE 754 Encoding Examples
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1731
Finite-Precision Arithmetic
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1732
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1733
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1734
Section 6.5
Interval Arithmetic
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1735
Interval Arithmetic
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1736
Applications of Interval Arithmetic
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1737
Real Interval Arithmetic
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1738
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:
2 x ≥ b ⇒ −x ≤ −b and
1 1
2 x ≤ b ⇒ −x ≥ −b
2 2
■ subtraction:
A − B = [a1 , a2 ] − [b1 , b2 ] = [a1 − b2 , a2 − b1 ]
■ formula for subtraction follows from combining addition and negation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1739
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 ̸∈ [b1 , b2 ],
x ∈ [b1 , b2 ], b1 , b2 all have same sign (implying b1 x > 0 and b2 x > 0) and
consequently:
2 x ≥ b1 ⇒ x ≥
b1
b1 x b1 x ⇒ 1/b1 ≥ 1/x ⇒ 1/x ≤ 1/b1
x b2
2 x ≤ b2 ⇒ ≤ ⇒ 1/b2 ≤ 1/x ⇒ 1/x ≥ 1/b2
b2 x 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–2019 Michael D. Adams C++ Version: 2019-02-04 1740
Allowing Division By Interval Containing Zero
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1741
Allowing Division By Interval Containing Zero (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1742
Floating-Point Interval Arithmetic
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1743
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–2019 Michael D. Adams C++ Version: 2019-02-04 1744
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–2019 Michael D. Adams C++ Version: 2019-02-04 1746
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–2019 Michael D. Adams C++ Version: 2019-02-04 1747
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]
a1 ≥ 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–2019 Michael D. Adams C++ Version: 2019-02-04 1748
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 , +∞) (−∞, +∞)
a1 ≥ 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–2019 Michael D. Adams C++ Version: 2019-02-04 1749
Division
A/B, 0 ̸∈ 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–2019 Michael D. Adams C++ Version: 2019-02-04 1750
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–2019 Michael D. Adams C++ Version: 2019-02-04 1751
Comparisons
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1752
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–2019 Michael D. Adams C++ Version: 2019-02-04 1755
Section 6.5.1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1756
Geometric Predicates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1757
Filtered Geometric Predicates
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1758
Two-Dimensional Orientation Test
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1759
Example: Two-Dimensional Orientation Test
e b
= (0, 2) = (2, 2)
d
= (1, 1)
a c
= (0, 0) = (2, 0)
[0 2 2] [ −2 0 ]
■ orient2d(a, b, c) = det 020 = det 0 2 = −4 < 0; c is right of oriented
111
line through a and b
[0 2 1] [ −1 1 ]
■ orient2d(a, b, d) = det 021 = det −1 1 = 0; d is on oriented line
111
through a and b
[0 2 0]
0 2
[ ]
■ orient2d(a, b, e) = det 022 = det −2 0 = 4 > 0; e is left of oriented
111
line through a and b
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1760
Polygon Convexity Test
c b a c
c
b a
b
a
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1761
Three-Dimensional Orientation Test
d
b
a
e
= (1, 1, 0)
b
a = (2, 0, 0)
= (0, 0, 0)
f
= (1, 1, −2)
[0 2 2 1] [ −1 1 1
]
■ orient3d(a, b, c, d) = det 0021 = det −1 −1 1 = −8 < 0; d above
0002 −2 −2 −2
1111
oriented plane through a[
, b, and]c
0 2 2 1 [ −1 1 1
]
■ orient3d(a, b, c, e) = det 0 0 2 1 = det −1 −1 1 = 0; e lies in oriented
0 0 0 0
1 1 1 1 0 0 0
plane through a, b, and c
[0 2 2 1
] [ −1 1 1
]
■ orient3d(a, b, c, f ) = det 002 1 = det −1 −1 1 = 8 > 0; f below
0 0 0 −2
111 1 2 2 2
oriented plane through a, b, and c
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1763
Side-of-Oriented-Circle Test
c
d
positive negative
side side
a b
a b
= (0, 0) = (2, 0)
[ 0 2 0 1 ]
0 0 2 1
■ inCircle(a, b, c, d) = det 02 +02 22 +02 02 +22 12 +12 = 8 > 0; d on positive
1 1 1 1
side of oriented circle through a, b, and c (i.e., d inside circle)
[ 0 2 0 2 ]
0 0 2 2
■ inCircle(a, b, c, e) = det 02 +02 22 +02 02 +22 22 +22 = 0; e on oriented circle
1 1 1 1
through a, b, and c
[ 0 2 0 3 ]
0 0 2 3
■ inCircle(a, b, c, f ) = det 02 +02 22 +02 02 +22 32 +32 = −24 < 0; f on
1 1 1 1
negative side of oriented circle through a, b, and c (i.e., f outside circle)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1765
Preferred-Direction Test
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–2019 Michael D. Adams C++ Version: 2019-02-04 1766
Example: Preferred-Direction Test
u
d b = (2, 1)
= (0, 2) = (2, 2)
v w
a c = (1, 0) = (−1, 2)
= (0, 0) = (2, 0)
■ prefDir(a, b, c, d, u) =
|(−2, 2)|2 [(2, 2) · (2, 1)]2 − |(2, 2)|2 [(−2, 2) · (2, 1)]2 = 256 > 0; ab closer
than cd to direction of u
■ prefDir(a, b, c, d, v) =
|(−2, 2)|2 [(2, 2) · (1, 0)]2 − |(2, 2)|2 [(−2, 2) · (1, 0)]2 = 0; ab and cd
equally close to direction of v
■ prefDir(a, b, c, d, w) =
|(−2, 2)|2 [(2, 2) · (−1, 2)]2 − |(2, 2)|2 [(−2, 2) · (−1, 2)]2 = −256 < 0; cd
closer than ab to direction of w
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1767
Triangulations
Triangulation
Triangulation Invalid Triangulation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1768
Delaunay Triangulations
Delaunay
Triangulation Non-Delaunay
Delaunay Triangulation Triangulation Showing
Showing Circumcircles Violation of Circumcircle
Condition
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1769
Comments on Delaunay Triangulations
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1770
Edge Flips
vℓ e vj −→ vℓ
e′
vj
vi vi
■ number
(n) n2 of different triangulations of n vertices upper bounded by
−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–2019 Michael D. Adams C++ Version: 2019-02-04 1771
Locally-Delaunay Test
c
d
e
a b
■ given flippable edge e in triangulation with incident faces abc and bdc
whose vertices are specified in CCW order (and whose union is strictly
convex quadrilateral), determine if e is locally Delaunay
■ result can be determined using side-of-oriented-circle test
{
1 inCircle(a, b, c, d) ≤ 0
■ localDelaunay(a, b, c, d) =
0 inCircle(a, b, c, d) > 0
■ if localDelaunay(a, b, c, d) ̸= 0, edge e is locally Delaunay
■ if every flippable edge in triangulation is locally Delaunay, triangulation is
Delaunay
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1772
Locally Preferred-Directions Delaunay Test
c
d
e v
a b u
■ given flippable edge e in triangulation with incident faces abc and bdc
whose vertices are specified in CCW order (and whose union is strictly
convex quadrilateral), determine if e is locally preferred-directions
Delaunay with first and second direction vectors u and v, respectively
(where u and v are nonzero and neither parallel nor orthogonal)
■ result can be determined using side-of-oriented-circle and
preferred-direction tests
⎧b, c, d, u, v)
■ α(a,
⎨1 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–2019 Michael D. Adams C++ Version: 2019-02-04 1773
Locally Preferred-Directions Delaunay Test (Continued)
■ 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
■ if (and only if) localPrefDirDelaunay(a, b, c, d, u, v) ̸= 0, edge e is locally
preferred-directions Delaunay with first and second direction vectors u and
v, respectively
■ if every flippable edge in triangulation is locally preferred-directions
Delaunay, triangulation is preferred-directions Delaunay
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1774
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–2019 Michael D. Adams C++ Version: 2019-02-04 1775
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–2019 Michael D. Adams C++ Version: 2019-02-04 1776
Section 6.5.2
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1777
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1778
References II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1779
References III
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1780
Section 6.6
Cache-Efficient Code
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1781
The Memory Latency Problem
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1782
Section 6.6.1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1783
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–2019 Michael D. Adams C++ Version: 2019-02-04 1784
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–2019 Michael D. Adams C++ Version: 2019-02-04 1785
Caches
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1787
Block Placement
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1789
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–2019 Michael D. Adams C++ Version: 2019-02-04 1790
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–2019 Michael D. Adams C++ Version: 2019-02-04 1791
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–2019 Michael D. Adams C++ Version: 2019-02-04 1792
Block Identification
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1793
Decomposition of Memory Address
ηA bits
Memory Address
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1794
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:
2 if not fully associative, determined by index
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1795
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–2019 Michael D. Adams C++ Version: 2019-02-04 1796
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,
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1799
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–2019 Michael D. Adams C++ Version: 2019-02-04 1801
Translation Lookaside Buffer (TLB)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1802
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–2019 Michael D. Adams C++ Version: 2019-02-04 1804
Virtually-Indexed Physically-Tagged (VIPT) Caches
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1805
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–2019 Michael D. Adams C++ Version: 2019-02-04 1806
Cache Performance
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1807
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:
2 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:
2 256 KB, 8-way set associative, per core, pseudo-LRU replacement,
physically indexed (and tagged)
■ L3 cache:
2 2 MB per core, 16-way set associative, pseudo-LRU replacement (with
ordered selection algorithm), physically indexed (and tagged)
■ first-level TLB:
2 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:
2 512 entries, 4-way set associative, pseudo-LRU replacement, 4 KB page
size
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1808
ARM Cortex A8
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1809
Section 6.6.2
Cache-Efficient Algorithms
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1810
Cache-Efficient Algorithms
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1811
Code Transformations to Improve Cache Efficiency
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1812
Array Merging Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1813
Loop Interchange Example
■ before blocking:
1 // compute c := c + a b, where a, b, c are N-by-N
2 // matrices
3 template <class T, int N>
4 void naive_multiply(const T (&a)[N][N], const T (&b)[N][N],
5 T (&c)[N][N]) {
6 for (int i = 0; i < N; ++i) {
7 for (int j = 0; j < N; ++j) {
8 double s = 0;
9 for (int k = 0; k < N; ++k)
10 {s += a[i][k] * b[k][j];}
11 c[i][j] += s;
12 }
13 }
14 }
■ want to partition computation into blocks of size B×B, where B chosen so
that each block fits in cache
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1816
Blocking Example (Continued 1)
■ after blocking (with blocking factor B):
1 // compute c := c + a b, where a, b, c are N-by-N
2 // matrices
3 template <int B, class T, int N>
4 void blocked_multiply(const T (&a)[N][N], const T (&b)[N][N],
5 T (&c)[N][N]) {
6 for (int kk = 0; kk < N; kk += B) {
7 for (int jj = 0; jj < N; jj += B) {
8 for (int i = 0; i < N; ++i) {
9 for (int j = jj; j < std::min(jj + B, N); ++j) {
10 double s = 0;
11 for (int k = kk; k < std::min(kk + B, N); ++k)
12 {s += a[i][k] * b[k][j];}
13 c[i][j] += s;
14 }
15 }
16 }
17 }
18 }
■ performing computation using blocking significantly improves locality
■ potentially many fewer cache misses
■ unfortunately, code using blocking much less readable (i.e., more difficult
to understand)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1817
Blocking Example (Continued 2)
kk jj jj
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–2019 Michael D. Adams C++ Version: 2019-02-04 1818
Cache-Aware Versus Cache-Oblivious Algorithms
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1819
Section 6.6.3
Cache-Oblivious Algorithms
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1820
Tall Caches
■ suppose that cache has size M with block size B and N = M/B entries
■ cache is said to be tall if N > c′ B for some sufficiently large constant
c′ ≥ 1; otherwise, said to be short
■ essentially, tall property ensures that N exceeds B by large enough
margin that any (possibly non-contiguous) data of size D is guaranteed to
fit in cache if D ≤ M
■ that is, if size of some data does not exceed cache size, then that data
must fit in cache
■ this is not the case for short caches
■ for example, n × n block of elements inside larger array stored in
2
row-major . . . . . with n < M will not necessarily fit in cache if cache is
. . . . . . . . . . . .order
short
n
n
N
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1821
Idealized Cache Model
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1822
Remarks on Assumption of Optimal-Replacement Policy
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1823
Cache-Oblivious Algorithms
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1824
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 (i.e., incurs only minimum number of cache
misses)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1825
Array Reversal
1 2 3 4 5
a0 a1 a2 a3 a4 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 (i.e., ⌊N/2⌋ swap operations)
■ assuming at least two blocks fit in cache, incurs either ⌈N/B⌉ + 1 or
⌈N/B⌉ cache misses (i.e., Θ(N/B) cache misses)
■ cache oblivious and optimal
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1826
Naive Matrix Transposition
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1827
Naive Matrix Transposition: Performance
i
j
m i n j
n m
a b
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1829
Cache-Oblivious Matrix Transposition (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1830
Cache-Oblivious Matrix Transposition Example 1A
[ ] [ ]T
b11 b12 a a
= 11 12
b21 b22 a21 a22
[ ]T [ ]T
a a
b11 b12 = 11 b21 b22 = 12
[ ] [ ]
a21 a22
[ ] [ ]T [ ] [ ]T [ ] [ ]T [ ] [ ]T
b11 = a11 b12 = a21 b21 = a12 b22 = a22
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1831
Cache-Oblivious Matrix Transposition Example 1B
[ ] [ ]T
b11 b12 a a
= 11 12
b21 b22 a21 a22
[ ] [ ]
b11 [ ]T b12 [ ]T
= a11 a12 = a21 a22
b21 b22
[ ] [ ]T [ ] [ ]T [ ] [ ]T [ ] [ ]T
b11 = a11 b21 = a12 b12 = a21 b22 = a22
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1832
Cache-Oblivious Matrix Transposition Example 2
⎡ ⎤
b11 b12 [ ]T
⎣b21 b22 ⎦ = a11 a12 a13
a21 a22 a23
b31 b32
[ ] [ ]T [ ]T
b11 b12 a a a
= 11 12 b31 b32 = 13
[ ]
b21 b22 a21 a22 a23
[ ]T [ ]T
a a [ ] [ ]T [ ] [ ]T
b11 b12 = 11 b21 b22 = 12
[ ] [ ]
b31 = a13 b32 = a23
a21 a22
[ ] [ ]T [ ] [ ]T [ ] [ ]T [ ] [ ]T
b11 = a11 b12 = a21 b21 = a12 b22 = a22
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1833
Cache-Oblivious Matrix Transposition: Performance
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1834
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
m i n k m i
n p p
a b c
■ cache block holds L 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/L + mnp + mp/L) cache misses, which is Θ(n3 )
in case of square matrices
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1836
Cache-Oblivious Matrix Multiplication
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1837
Cache-Oblivious Matrix Multiplication (Continued 1)
■ case 1. if m = max{m, n, p} (i.e., number of rows in A largest):
2 decompose problem as follows:
[ ] [ ] [ ] [ ]
C1 A1 A1 A1 B
let C = and A = ; so AB = B=
C2 A2 A2 A2 B
2recurse to compute C1 := C1 + A1 B and C2 := C2 + A2 B
■ case 2. if n = max{m, n, p} (i.e., number of columns in A and rows in B
largest):
2 decompose problem as follows:
[ ] [ ]
B ] B1
A2 and B = 1 ; so AB = A1
[ ] [
let A = A1 A2 = A1 B1 + A2 B2
B2 B2
recurse to compute C := C + A1 B1 and then C := C + A2 B2
2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1839
Cache-Oblivious Matrix Multiplication Example 1
[ ] [ ][ ]
c11 c12 a11 a12 b11 b12
+=
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–2019 Michael D. Adams C++ Version: 2019-02-04 1840
Cache-Oblivious Matrix Multiplication Example 2
⎡ ⎤
] b b12
a13 ⎣ 11
[ ] [
c11 c12 a11 a12
+= b21 b22 ⎦
c21 c22 a21 a22 a23
b31 b32
[ ] [ ][ ] [ ] [ ]
c11 c12 a11 a12 b11 b12 c11 c12 a13 [ ]
+= += b31 b32
c21 c22 a21 a22 b21 b22 c21 c22 a23
[ ] [ ]
c11 c[12 += ] c21 c[22 += ] [ ] [ ]
[ ] b11 b12 [ ] b11 b12 [ c11] [ c12 += ] [ c21] [ c22 += ]
a11 a12 a21 a22 a13 b31 b32 a23 b31 b32
b21 b22 b21 b22
[ ] [ ] [ ] [ ]
c11 += c12 += c21 += c22 += [ ] [ ] [ ] [ ]
[ c11] [ += ] [ c12] [ += ] [ c21] [ += ] [ c22] [ += ]
[ ] [ ] [ ] [ ]
[ ] b11 [ ] b12 [ ] b11 [ ] b12
a11 a12 a11 a12 a21 a22 a21 a22 a13 b31 a13 b32 a23 b31 a23 b32
b21 b22 b21 b22
[ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]
[ c11] [ += ] [ c11] [ += ] [ c12] [ += ] [ c12] [ += ] [ c21] [ += ] [ c21] [ += ] [ c22] [ += ] [ c22] [ += ]
a11 b11 a12 b21 a11 b12 a12 b22 a21 b11 a22 b21 a21 b12 a22 b22
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1841
Cache-Oblivious Matrix Multiplication: Performance
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1842
Cache-Oblivious Matrix Multiplication Revisited
[ ] [ ][ ]
c11 c12 a11 a12 b11 b12
=
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–2019 Michael D. Adams C++ Version: 2019-02-04 1844
Cache-Oblivious Matrix Multiplication Revisited Example 2
⎡ ⎤
] b b12
a13 ⎣ 11
[ ] [
c11 c12 a11 a12
= b21 b22 ⎦
c21 c22 a21 a22 a23
b31 b32
[ ] [ ][ ] [ ] [ ]
c11 c12 a11 a12 b11 b12 c11 c12 a13 [ ]
= += b31 b32
c21 c22 a21 a22 b21 b22 c21 c22 a23
[ ] [ ]
c11 [c12 = c21 [c22 = [ ] [ ]
[ c11] [ c12 += ] [ c21] [ c22 += ]
] ]
[ ] b11 b12 [ ] b11 b12
a11 a12 a21 a22 a13 b31 b32 a23 b31 b32
b21 b22 b21 b22
[ ] [ ] [ ] [ ]
c11 = c12 = c21 = c22 = [ ] [ ] [ ] [ ]
[ c11] [ += ] [ c12] [ += ] [ c21] [ += ] [ c22] [ += ]
[ ] [ ] [ ] [ ]
[ ] b11 [ ] b12 [ ] b11 [ ] b12
a11 a12 a11 a12 a21 a22 a21 a22 a13 b31 a13 b32 a23 b31 a23 b32
b21 b22 b21 b22
[ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]
[ c11
][ = ] [ c11] [ += ] [ c12
][ = ] [ c12] [ += ] [ c21
][ = ] [ c21] [ += ] [ c22
][ = ] [ c22] [ += ]
a11 b11 a12 b21 a11 b12 a12 b22 a21 b11 a22 b21 a21 b12 a22 b22
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1845
Strassen’s Algorithm for Matrix Multiplication
j2 =0 j1 =0
■ in preceding equation, inner and outer summations are DFTs
■ operationally, computation specified in above equation can be performed
by:
1 computing n DFTs of size n (i.e., inner summation)
2 1
−i j
2 multiplying result by factors ωn 1 2 (called twiddle factors)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1847
Cache-Oblivious Fast Fourier Transform (FFT)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1848
Example: Four-Point DFT
[ ] [ ]
■ Note: The DFT of x0 x1 is easily shown to be x0 + x1 x0 − x1 .
■ Consider computing the DFT of the following sequence of length n = 4:
[ ]
x0 x1 x2 x3 .
■ Factor n as n1 n2 , where n1 = n2 = 2.
■ Treat the one-dimensional array of size 4 as the following 2 × 2 array
stored in row-major order:
[ ]
x0 x1
.
x2 x3
■ Transpose the matrix to obtain:
[ ]
x0 x2
.
x1 x3
■ Replace each row of the matrix by its two-point DFT to yield:
[ ]
x0 + x2 x0 − x2
.
x1 + x3 x1 − x3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1849
Example: Four-Point DFT (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1850
Example: Four-Point DFT (Continued 2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1851
Cache-Oblivious FFT: Performance
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1852
Section 6.6.4
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1853
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1854
References II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1855
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–2019 Michael D. Adams C++ Version: 2019-02-04 1856
Section 6.7
Vectorization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1857
Section 6.7.1
Vector Processing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1858
Vector Processing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1859
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–2019 Michael D. Adams C++ Version: 2019-02-04 1860
Vector-Memory and Vector-Register Architectures
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1861
Vector-Register Architectures
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1862
Vector Extensions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1863
Intel x86/x86-64 Streaming SIMD Extensions (SSE)
XMM15
■ each vector register can be used to hold:
2 16 8-bit bytes
2 8 16-bit integers
2 4 32-bit integers
2 2 64-bit integers
2 4 32-bit single-precision floating-point numbers
2 2 64-bit double-precision floating-point numbers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1864
Intel x86/x86-64 Advanced Vector Extensions (AVX)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1865
ARM NEON
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1866
Checking for Processor Vector Support on Linux
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1867
Section 6.7.2
Code Vectorization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1868
Vectorization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1869
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–2019 Michael D. Adams C++ Version: 2019-02-04 1870
Approaches to Vectorization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1871
Auto-Vectorization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1872
GCC Compiler and Vectorization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1873
GCC Compiler Options Related to Vectorization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1874
GCC Compiler Options Related to Vectorization (Continued)
■ -fopenmp
2 enable OpenMP support (which requires GOMP library)
■ -fopenmp-simd
2 enable OpenMP SIMD support (which does not require run-time library)
■ -S
2 produce assembly language output only (instead of object code)
■ -fverbose-asm
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1875
Clang Compiler and Vectorization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1876
Clang Compiler Options Related to Vectorization
■ -fvectorize and -fno-vectorize
2 enable and disable loop vectorizer, respectively
■ -fslp-vectorize and -no-fslp-vectorize
2 enable and disable SLP vectorizer, respectively
■ -fslp-vectorize-aggressive
2 enable more aggressive vectorization in SLP vectorizer
■ -Rpass=loop-vectorize
2 enable remarks that identify loops that were successfully vectorized
■ -Rpass-missed=loop-vectorize
2 enable remarks that identify loops that failed vectorization and indicate if
vectorization specified
■ -Rpass-analysis=loop-vectorize
2 enable remarks that identify statements that caused vectorization to fail
■ -fopenmp
2 enable OpenMP support (which requires OMP library)
■ -S
2 produce assembly language output only (instead of object code)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1877
Assessing Quality of Vectorized Code
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1878
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–2019 Michael D. Adams C++ Version: 2019-02-04 1879
Auto-Vectorization with Hints
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1880
Obstacles to Vectorization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1881
Data Dependencies and Vectorization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1882
Flow Dependencies
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1883
Flow Dependence Example 1
■ 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–2019 Michael D. Adams C++ Version: 2019-02-04 1884
Flow Dependence Example 1: 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–2019 Michael D. Adams C++ Version: 2019-02-04 1885
Flow Dependence Example 1: Vectorized Loop
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1886
Flow Dependence Example 2
■ 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–2019 Michael D. Adams C++ Version: 2019-02-04 1887
Output Dependencies
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1888
Control-Flow Dependencies and Vectorization
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1889
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–2019 Michael D. Adams C++ Version: 2019-02-04 1890
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–2019 Michael D. Adams C++ Version: 2019-02-04 1891
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:
2 generate two different versions of code for loop, one without vectorization
for aliasing case and one with vectorization for case of no aliasing
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1892
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:
2 GCC and Clang support __restrict__ keyword
2 MSVC supports __restrict keyword
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1894
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–2019 Michael D. Adams C++ Version: 2019-02-04 1895
Handling Misaligned Data
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1896
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–2019 Michael D. Adams C++ Version: 2019-02-04 1897
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–2019 Michael D. Adams C++ Version: 2019-02-04 1898
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–2019 Michael D. Adams C++ Version: 2019-02-04 1899
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 correctly
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–2019 Michael D. Adams C++ Version: 2019-02-04 1900
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–2019 Michael D. Adams C++ Version: 2019-02-04 1901
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–2019 Michael D. Adams C++ Version: 2019-02-04 1902
Vectorization Example: Invoking add Function
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1904
OpenMP SIMD Constructs
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1905
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:
2 induction variable has integer, pointer, or random-access iterator type
2 limited test and increment/decrement for induction variable
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1906
OpenMP declare simd Pragma
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1907
OpenMP SIMD-Related Pragma Clauses
■ safelen(length)
2 specifies length as maximum number of iterations that can be run
concurrently in safe manner (i.e., without data-dependence problems)
■ collapse(n)
2 specifies how many (nested) loops to associate with loop construct (i.e.,
how many nested loops to combine)
■ simdlen(length)
2 specifies length as preferred length of vector registers used
■ aligned(argument-list[:alignment])
2 specifies items in argument-list as having given alignment (e.g., alignment)
■ uniform(argument-list)
2 indicates each argument in argument-list has constant value between
iterations of given loop (i.e., constant value across all SIMD lanes)
■ inbranch
2 specifies that function will always be called from inside conditional
statement of SIMD loop
■ notinbranch
2 specifies that function will never be called from inside conditional statement
of SIMD loop
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1908
OpenMP SIMD-Related Pragma Clauses (Continued)
■ linear(list[:linear-step])
2 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)
2 declares variables in list to be private to each iteration
■ lastprivate(list)
2 declares variables in list to be private to each iteration, and last value is
copied out from last iteration instance
■ reduction(operator:list)
2 specifies variables in list are reduction variables for operator operator
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1909
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–2019 Michael D. Adams C++ Version: 2019-02-04 1910
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–2019 Michael D. Adams C++ Version: 2019-02-04 1911
Section 6.7.3
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1912
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1913
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1914
Section 6.8
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1915
Documentation for Software Development
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1916
Software Requirements Specification (SRS)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1917
SRS (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1918
External Interfaces
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1919
Benefits of SRS
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1920
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):
2 records delimited by single newline character
2 each record consists of one or more fields, separated by one or more
whitespace characters
■ restrictions/constraints:
2 may assume sufficient memory to buffer all records
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1921
SRS Example: Sorting Program (Continued)
■ command-line interface:
sort [-r] [-k $n] [-n]
■ supported command-line options:
Option Description
-k $n Sort using nth field in record; if not specified, n
defaults to 1.
-n Treat key as real number (instead of string) for
sorting purposes; if not specified, key treated as
string.
-r Sort in descending (instead of ascending) order; if
not specified, defaults to ascending order.
■ give examples illustrating expected use cases
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1922
Software Design Description (SDD)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1923
SDD (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1924
Benefits of SDD
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1925
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:
2 constructor that creates dataset by reading all records from input stream
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 1926
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–2019 Michael D. Adams C++ Version: 2019-02-04 1927
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1928
Section 6.9
Software Testing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1929
Software Testing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1930
Examples of Well-Known Software Failures
■ Therac-25 Radiation Overdoses
2 from 1985 to 1987, at least six incidents in which patients being treated with
Therac-25 radiation-therapy machine received massive overdoses of
radiation (as much as 100 times intended dosage); at least three patients
died
2 failure due to race condition in control software
■ Northeast Blackout of 2003
2 in 2003, widespread blackout occurred affecting several states/provinces in
USA and Canada (i.e., New York, New Jersey, Maryland, Connecticut,
Massachusetts, Ohio, and Ontario) with outage lasting from hours to days
2 at time, world’s second most widespread blackout in history
2 blackout contributed to almost 100 deaths
2 triggered by local outage that went undetected due to race condition in
monitoring software
■ Ariane 5 Rocket Explosion
2 in 1996, unmanned Ariane 5 launched by European Space Agency
exploded 37 seconds after lift off
2 cause of failure was numerical overflow in inertial guidance system software
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1931
Examples of Well-Known Software Failures (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1932
Basic Terminology
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1933
Some Types of Faults
■ input/output faults
2 correct input not accepted; incorrect input accepted; wrong format;
cosmetic; incomplete or missing result
■ interface faults
2 parameter mismatch; incompatible types; call to wrong procedure; call to
nonexistent procedure
■ logic faults
2 missing or duplicate cases; missing or extraneous conditions; incorrect loop
iteration; incorrect operator; infinite recursion
■ data faults
2 using uninitialized variable; dereferencing null pointer; incorrect
initialization; wrong variable used; wrong data reference; incorrect
subscript; incorrect data scope; inconsistent data; scaling or units error
■ computation faults
2 incorrect algorithm; missing computation; incorrect operation; arithmetic
overflow or underflow; insufficient precision
■ multithreading faults
2 race condition; deadlock
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1934
Testing Methods
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1935
Code Reviews
■ code review is process whereby source code examined by one or more
people in effort to identify bugs or other problems
■ variety of terms used to refer to code reviews (e.g., code inspections and
code walkthroughs) but no widely accepted meaning for such terms
■ code reviews can be applied at almost any stage of software development
■ code review typically has several participants with original author often
being one
■ code review meeting might last couple of hours
■ may involve some preparatory work prior to meeting (e.g., examining
source code)
■ code review process can take many forms, such as:
2 read code line by line looking for errors
2 select some test cases and then, for each test case, step through code as if
being executed by computer
■ may be formal written documentation to complete as part of review
■ code reviews often find actual bug as opposed to failure resulting from bug
■ code reviews can be effective at finding certain types of errors
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1936
Static Analysis Tools
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1937
Examples of Static Analysis Tools
■ Clang
. . . . . . .Tidy
....
■ Clang Static Analyzer
.......................
■ CppCheck (http://cppcheck.sourceforge.net)
■ Cpplint (http://github.com/cpplint/cpplint)
■ many commercial products also available, such as:
2 Coverity Scan (http://scan.coverity.com), which is free for use in
open-source projects
2 CppDepend (http://www.cppdepend.com)
2 Klocwork
(http://www.roguewave.com/products-services/klocwork)
2 PVS-Studio Analyzer (http://www.viva64.com/en/pvs-studio)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1938
Black-Box Testing (a.k.a. Functional Testing)
■ black-box testing (also known as functional testing) is testing method
that checks if software meets its specifications without using knowledge
of its internal structure
■ concentrates on finding circumstances in which software does not behave
according to its specifications
■ test cases derived solely from specifications
■ advantages:
2 tester does not need to know internal structure of program
2 test cases can be created as soon as functional specifications complete
2 tester needs no knowledge of implementation, including specific
programming languages
2 tests will be performed from user’s point of view
2 testing can help to expose ambiguities or inconsistencies in specifications
2 as tester and developer are independent of each other, testing balanced
and unprejudiced
■ disadvantages:
2 may leave many program paths untested
2 high probability of repeating tests already performed by developer
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1939
White-Box Testing (a.k.a. Structural Testing)
■ white-box testing (also known as structural testing) is testing method
that employs knowledge of internal structure of software under test
■ tester has access to source code and possibly documentation describing
software internals, such data structures and algorithms used
■ derive testing strategy based on examination of internal structure of
software
■ typically choose tests to exercise as much of code and control flow as
possible
■ advantages:
2 more thorough testing possible than with black-box testing
2 forces developer to reason more carefully about implementation
■ disadvantages:
2 as details of code are considered, skilled testers required with knowledge of
programming
2 tests more sensitive to changes in underlying implementation (and may be
rendered useless due to implementation changes)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1940
Gray-Box Testing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1941
Equivalence-Class Testing (a.k.a. Equivalence Partitioning)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1943
Equivalence-Class Testing (Continued 2)
■ advantages:
2 equivalence classes allow many inputs to be tested using one
representative element from equivalence class, greatly reducing number of
test cases
2 since equivalence classes are disjoint, can eliminate/reduce redundancy in
tests
■ disadvantages:
2 just because members of equivalence class should in theory behave
similarly does not means that they actually will in practice (e.g., due to
unanticipated bugs)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1944
Equivalence-Class Testing Example [Volume Discount]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1945
Equivalence-Class Testing Example [Safe Add]
■ consider function that computes sum of two integers safely (i.e., ensuring
that overflow does not occur) and has declaration:
std::pair<int, bool> safe_add(int x, int y);
■ one possible choice for equivalence classes:
1 overflow in positive direction: x ≥ 0 and y ≥ 0 and y > INT_MAX − x
3 no overflow:
2 x ≥ 0 and y < 0; or x < 0 and y ≥ 0; or
2 x ≥ 0 and y ≥ 0 and y ≤ INT_MAX − x; or
2 x < 0 and y < 0 and y ≥ INT_MIN − x
y
INT_MAX
x
INT_MIN INT_MAX
INT_MIN
■ could then use three test cases, one from each equivalence class
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1946
Boundary-Value Testing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1947
Boundary-Value Testing Example [Volume Discount]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1948
Boundary-Value Testing Example [Safe Add]
■ again, consider function that computes sum of two integers safely (i.e.,
ensuring that overflow does not occur) and has declaration:
std::pair<int, bool> safe_add(int x, int y);
■ as shown in figure, can partition input domain into three equivalence
classes (i.e., overflow in positive direction, overflow in negative direction,
and no overflow):
y
INT_MAX
x
INT_MIN INT_MAX
INT_MIN
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1949
Special-Value Testing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1950
Special-Value Testing (Continued)
■ example: some test cases for binary search in container (e.g., array)
might include:
2 exactly one element in container being searched
2 container size is power of 2
2 container size is one greater and one less than power of 2
2 query element in container
2 query element not in container
■ example: to test algorithm that calculates sinc function, might use
knowledge that handling cases of computing sinc(x) = sin(x)/x for x = 0
and x ̸= 0 likely to be performed differently, leading to at least two test
cases:
2 test for both zero and nonzero values
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1951
Random Testing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1953
Control-Flow Graph Examples: If-Else and Switch
declarations:
1 bool c; double x; double y;
1 if (c) {
2 4 2 y = x * x;
3 } else {
4 y = x;
6 5 }
6 // ...
declarations:
int n; double x; double y;
1 1 switch (n) {
2 case 0:
3 y = 0.0;
4 break;
3 6 9 5 case 1:
6 y = 2.0 * x;
7 break;
8 case 2:
12 9 y = 0.5 * x * x;
10 break;
11 }
12 // ...
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1954
Control-Flow Graph Examples: While and Do-While Loops
1
declarations:
int n;
2 1 while (n > 0) {
2 --n;
3 }
4 // ...
4
2
declarations:
int n;
3 1 do {
2 --n;
3 while (n > 0);
4 // ...
4
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1955
Control-Flow Graph Examples: For Loop
1a
1b
declarations:
int a[1024];
2 1 for (int i = 0; i < 1024; ++i) {
2 a[i] = 0;
3 }
4 // ...
1c
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1956
Structural Coverage Analysis
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1957
Examples of Structural-Coverage Criteria
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1958
Function Coverage
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1959
Statement Coverage
■ statement coverage ensures that every statement executed at least once
■ statement coverage ensures that each node in control-flow graph visited
during testing (consequently, also called node coverage)
■ level of statement coverage can be measured as number of (distinct)
statements executed divided by total number of (distinct) statements in
program
■ statement coverage alone not particularly strong coverage criterion
■ some weaknesses of statement coverage include:
2 does not usually test all branch outcomes
2 predicates may be tested for only one value
2 loop bodies may only be executed once
■ statement coverage for following code fragment can be achieved with
single test case (e.g., (a, b) = (1, 1)):
if (a > 0)
++x;
if (b > 0)
--x;
■ in above example, statement coverage achieved with single test case
does not exercise all possible branch outcomes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1960
Conditions and Decisions
■ condition: boolean expression containing no boolean operators (i.e.,
boolean expression that cannot be decomposed into boolean
subexpressions)
■ decision: boolean expression composed of conditions and zero or more
boolean operators (or integral expression in case of switch statement)
■ decisions associated with branching (i.e., control-flow) constructs (e.g.,
if, switch, for, while, do, and ternary operator)
■ example:
2 consider code fragment:
if (x > 0 && x < 1024) { /* ... */}
2 x > 0 is condition
2 x < 1024 is condition
2 x > 0 && x < 1024 is decision but not condition since can be
decomposed further
■ short-circuit evaluation of boolean expressions has implications in terms
of what constitutes decision, as short-circuit
. . . . . . . . . . . . .evaluation
. . . . . . . . . . . introduces
additional control flow
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1961
Decision Coverage (a.k.a. Branch Coverage)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1962
Decision Coverage Example [(a || b) && c]
■ consider following code fragment (with partial control-flow graph shown
earlier):
.......
if ((a || b) && c)
++x;
■ assuming no short-circuiting logic:
2 one decision ((a || b) && c) with three conditions, namely, a, b, and c
2 decision coverage requires at least two test cases (i.e., one test case for
each possible outcome of single binary decision), such as:
# a b c (a || b) && c
1 F F F F
2 T T T T
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1963
Condition Coverage
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1964
Condition/Decision Coverage
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1965
Condition/Decision Coverage Example [(a || b) && c]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1966
Modified Condition/Decision Coverage (MCDC)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1967
MCDC (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1968
MCDC Example [a || b]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1969
MCDC Example [a && b && c && d]
■ consider following code fragment:
if (a && b && c && d)
x = 42;
else
x = 0;
■ assuming no short-circuiting logic, unique-cause MCDC requires at least
5 test cases, such as:
# a b c d a && b && c && d
Independence Pairs
1 T T T T T
a (1, 2)
2 F T T T F
b (1, 3)
3 T F T T F
c (1, 4)
4 T T F T F
d (1, 5)
5 T T T F F
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1970
MCDC Example [(a || b) && c]
■ consider following code fragment (with partial control-flow graph shown
earlier):
.......
if ((a || b) && c)
++x;
■ assuming no short-circuiting logic, unique-cause MCDC requires at least
4 test cases, such as:
# a b c (a || b) && c
Independence Pairs
1 F F T F
a (1, 2)
2 T F T T
b (1, 3)
3 F T T T
c (2, 4)
4 T F F F
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1971
Multiple-Condition Coverage
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1972
Multiple-Condition Coverage Example [(a || b) && c]
■ consider following code fragment (with partial control-flow graph shown
earlier):
.......
if ((a || b) && c)
++x;
■ assuming no short-circuiting logic, multiple-condition coverage requires at
least 23 = 8 test cases (i.e., 1 test case for each possible combination of
outcomes from 3 conditions):
# a b c (a || b) && c # a b c (a || b) && c
1 F F F F 5 F F T F
2 T F F F 6 T F T T
3 F T F F 7 F T T T
4 T T F F 8 T T T T
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1973
All-Paths Coverage
■ all-paths coverage ensures that every path in program taken at least once
■ in other words, all-paths coverage ensures that every possible path from
source node to sink node in control-flow graph is taken during testing
■ all-paths coverage includes function, statement, decision, condition,
condition/decision, MCDC, and multiple-condition coverages
■ except in case of trivial programs, number of possible paths
astronomically large
■ testing every path infeasible except in very trivial programs
■ consequently, all-paths coverage of little practical utility
■ moreover, even if every path could be tested, not sufficient to test every
path through code, since:
2 testing every path may not guarantee that program meets all specifications
2 defects in code could be due to missing paths and non-existent paths
cannot be tested
2 whether bug manifests itself on particular path through code may depend
on program state at each step (i.e., data sensitivity)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1974
All-Paths Coverage Examples
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1975
Comparison of Various Structural-Coverage Types
Condition/ Multiple
Statement Decision Condition Decision MCDC Condition
Coverage Criterion Coverage Coverage Coverage Coverage Coverage Coverage
every point of entry and exit invoked at ✓ ✓ ✓ ✓ ✓
least once
every statement executed at least once ✓ ✓∗ ✓∗ ✓∗ ✓∗
every decision has taken all possible out- ✓ ✓ ✓ ✓
comes at least once
every condition in each decision has ✓ ✓ ✓ ✓
taken all possible outcomes at least once
every condition in each decision shown ✓ ✓
to independently affect that decision’s out-
come
every combination of condition outcomes ✓
within each decision invoked at least once
∗ not explicitly required in coverage definition but always implicitly satisfied
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1976
Data-Flow Coverage
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1977
Variable Definitions and Uses
■ each occurrence of variable in program classified as being either definition
or use
■ definition: occurrence of variable where its value is set (i.e., written)
std::string s; // definition of s (via construction)
i = 0; // definition of i
*p = 42; // definition of *p
■ use: occurrence of variable where its value is used (i.e., read)
■ each use can be classified as either being predicate use or computation
use
■ predicate use (P-use): use of variable in predicate (i.e., condition) of
branch statement; for example:
if (x > 0) {/* ... */} // P-use of x
while (n != 0) {/* ... */} // P-use of n
■ computation use (C-use): use of variable that is not P-use; for example:
y = x + 1; // C-use of x
y = sin(x); // C-use of x (pass by value parameter)
■ variable can be both used and re-defined in same statement; for example:
x = 3 * x + 2; // x read and then written
increment(x); // x incremented (via pass by reference)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1978
Definition-Clear and Definition-Use Paths
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1979
Define/Use Coverage Criteria
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1980
Power Example: Source Code
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1981
Power Example: Control-Flow Graph, Definitions, C-Uses, and P-Uses
initialize x, y; if (y >= 0)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1982
Difficulties With Data-Flow Coverage
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1983
Structural Coverage Criteria Subsumption Hierarchy
All Paths
Statement
Function
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1984
Structural Coverage in Various Standards
■ DO-178C — Software Considerations in Airborne Systems and
Equipment Certification
2 primary document used by certification authorities (such as FAA, EASA,
and Transport Canada) to approve all commercial software-based
aerospace systems
2 code that could result in catastrophic failure (e.g., crash) must have
statement, decision, and MCDC coverages
2 code that could result in hazardous failure (e.g., passenger fatality) must
have decision and statement coverages
2 code that could result in major failure (e.g., passenger injury) must have
statement coverage
■ ISO 26262 — Road vehicles — Functional safety
2 standard for functional safety of electrical and electronic systems in
production automobiles
2 for most critical systems, highly recommends decision and MCDC
coverages and recommends statement coverage
2 for least critical systems, highly recommends statement coverage and
recommends decision and MCDC coverages
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1985
Structural Coverage in Various Standards (Continued)
■ IEC 61508 — Functional Safety of Electrical/Electronic/Programmable
Electronic Safety-Related Systems
2 safety standard that applies to many industries
2 for most critical systems, highly recommends statement, decision, and
MCDC coverages
2 for least critical systems, recommends statement, decision, and MCDC
coverages
■ EN 50128 — Railway Applications — Communication, Signalling and
Processing Systems (a.k.a. IEC 62279)
2 pertains to programmable electronic systems used in railway control and
protection applications
2 for most critical systems, highly recommends statement, decision, and
MCDC coverage
■ IEEE 1008-1987 — IEEE Standard for Software Unit Testing
2 defines integrated approach to systematic and documented unit testing
2 specifies statement coverage as completeness requirement
2 recommends decision coverage for code that is critical or has inadequate
requirements specification
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1986
Structural Coverage in Practice
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1987
Dynamic Analysis Tools
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1988
Examples of Dynamic Analysis Tools
■ Address
. . . . . . . . . .Sanitizer
. . . . . . . . . .(ASan)
.......
■ Undefined-Behavior Sanitizer (UBSan)
.........................................
■ Thread
. . . . . . . . .Sanitizer
. . . . . . . . . (TSan)
.......
■ Memory Sanitizer (MSan)
...........................
■ Leak
. . . . . .Sanitizer
. . . . . . . . . .(LSan)
.......
■ Valgrind (Callgrind, Cachegrind, Helgrind, Memcheck, and Massif) and
......... .......... ......... ........... .......
KCacheGrind
..............
■ Gcov,
. . . . . . Lcov,
. . . . . Gcovr,
. . . . . . . and LLVM
. . . . . . Cov
....
■ Kcov
.....
■ Control Flow Integrity (CFI) Sanitizer
(https://clang.llvm.org/docs/ControlFlowIntegrity.html)
■ many commercial products also available, such as:
2 Testwell CTC++ (http://www.testwell.fi/ctcdesc.html)
2 Bullseye Coverage (http://www.bullseye.com)
2 PurifyPlus (http://teamblue.unicomsi.com/products/purifyplus)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1989
Testing Levels
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1990
Unit Testing
■ unit testing is stage of software testing in which individual components
(i.e., units) tested
■ first level of software testing; happens before integration testing
■ unit being tested typically corresponds to:
2 class or class method
2 function
■ goal is to isolate unit and validate its correctness
■ only ensures that unit works correctly in isolation
■ conducted by developer
■ usually uses white-box testing but may also employ gray-box and
black-box testing
■ can be done manually but is usually automated
■ unit testing allows problems to be found early in development cycle
■ unit testing might include (amongst other things): static code analysis,
running test cases in conjunction with structural-coverage analysis, and
code reviews
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1991
Integration Testing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1992
System Testing
■ system testing is testing of complete software product (i.e., consisting of
all integrated components) against specifications
■ third level of testing; occurs after integration testing and before
acceptance testing
■ performed before completed software product introduced to market
■ goal is to ensure that system meets its requirements
■ conducted by tester
■ employs black-box testing
■ examples of different types of testing that may be included in system
testing:
2 graphical user interface testing
2 usability testing
2 compatibility testing
2 performance testing
2 regression testing
2 installation testing
2 recovery testing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1993
Acceptance Testing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1994
Regression Testing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1995
Performance Testing
■ performance testing checks how software will behave and perform under
various workloads
■ may consider such factors as:
2 speed
2 resource usage (e.g., memory, disk, and network)
2 reliability
2 scalability
2 responsiveness
2 throughput
■ load testing checks how software behaves under anticipated workloads
■ stress testing checks how software behaves under extreme workloads
■ soak testing (also known as endurance testing) checks how well
software can handle expected workload over long periods of time
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1996
Fuzz Testing
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1997
Fuzzing Tools
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1998
Unit-Test Frameworks
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 1999
Examples of Unit-Test Frameworks
■ Catch2
........
■ Google Test (http://github.com/google/googletest)
■ Boost Test (http://www.boost.org)
■ many commercial products also available, such as:
2 Parasoft C/C++ Test (https://www.parasoft.com/products/ctest)
2 Testwell CTA++ (http://www.testwell.fi/ctadesc.html)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2000
Bugs in Concurrent Programs
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2001
Strategies for Testing Concurrent Programs
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2002
Strategies for Testing Concurrent Programs (Continued)
■ utilize data-race detection tools such as:
2 Thread Sanitizer (TSan)
2 Valgrind/Helgrind
■ test code on all platforms of interest (e.g., different processor
architectures and operating systems)
■ since implementations of synchronization primitives may differ across
operating systems, some bugs may show themselves only under certain
operating systems
■ some implementations of synchronization primitives (particularly those on
processor architectures with relatively strong memory models) may
provide more guarantees than what is requested by code, which can mask
bugs
■ suppose, for example, that code requires particular aligned 32-bit memory
access to be atomic in order to work correctly, but does not specifically
request access to be atomic, which is bug
■ preceding bug would never show itself on processor architecture where all
aligned 32-bit memory accesses are atomic
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2003
Section 6.9.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2004
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2005
References II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2006
References III
11 S. Rapps and E. J. Weyuker. Selecting software test data using data flow
information.
IEEE Trans. on Software Engineering, 11(4):367–375, Apr. 1985.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2007
References IV
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2008
Talks I
1 Marshall Clow. Making Your Library More Reliable with Fuzzing. C++Now,
Aspen, CO, USA, May 10, 2018. Available online at
https://youtu.be/LlLJRHToyUk.
2 Craig Young. Fuzz Smarter, Not Harder: An AFL Fuzz Primer. BSidesSF,
San Francisco, CA, USA, Feb. 28–29, 2016. Available online at
https://youtu.be/29RbO5bftwo.
3 Daniel Marjamaki. Cppcheck: Static Analysis of C++ Code.
SwedenCpp::Stockholm, Sundbyberg, Sweden, Dec. 11, 2018. Available
online at https://youtu.be/ATkTqBqjYBY.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2009
Part 7
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2010
Section 7.1
Debuggers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2011
Source-Level Debuggers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2012
GNU Debugger (GDB)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2013
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–2019 Michael D. Adams C++ Version: 2019-02-04 2014
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–2019 Michael D. Adams C++ Version: 2019-02-04 2015
GNU Data Display Debugger (DDD)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2016
Section 7.2
Code Sanitizers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2017
Code Sanitizers
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2018
Address Sanitizer (ASan)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2019
Using Address Sanitizer
2 new_delete_type_mismatch
2 exitcode
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2020
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–2019 Michael D. Adams C++ Version: 2019-02-04 2021
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–2019 Michael D. Adams C++ Version: 2019-02-04 2022
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–2019 Michael D. Adams C++ Version: 2019-02-04 2024
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–2019 Michael D. Adams C++ Version: 2019-02-04 2025
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–2019 Michael D. Adams C++ Version: 2019-02-04 2026
Double Free
double_free.cpp
1 int main() {
2 int* p = new int[16];
3 delete[] p;
4 delete[] p;
5 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2027
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–2019 Michael D. Adams C++ Version: 2019-02-04 2028
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–2019 Michael D. Adams C++ Version: 2019-02-04 2029
Thread Sanitizer (TSan)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2030
Using Thread Sanitizer
2 history_size
2 suppressions
2 exitcode
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2031
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–2019 Michael D. Adams C++ Version: 2019-02-04 2032
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::scoped_lock<std::mutex> l0(m0);
9 std::scoped_lock<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::scoped_lock<std::mutex> l1(m1);
16 std::scoped_lock<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–2019 Michael D. Adams C++ Version: 2019-02-04 2033
Deadlock (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2034
Memory Sanitizer (MSan)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2035
Using Memory Sanitizer
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2036
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–2019 Michael D. Adams C++ Version: 2019-02-04 2037
Undefined-Behavior Sanitizer (UBSan)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2038
Using Undefined-Behavior Sanitizer
2 verbosity
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2039
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–2019 Michael D. Adams C++ Version: 2019-02-04 2040
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–2019 Michael D. Adams C++ Version: 2019-02-04 2041
Leak Sanitizer (LSan)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2042
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–2019 Michael D. Adams C++ Version: 2019-02-04 2043
Section 7.2.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2044
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2045
Section 7.3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2046
Clang Static Analyzer
■ Clang Static Analyzer is static analysis tool for C/C++, which is part of
Clang
■ supports many checks (e.g., new-delete mismatch, assigning uninitialized
values, and so on)
■ interface provided mainly through two programs:
1 scan-build, which is used to invoke static analyzer when building code
■ can be used with most build processes (e.g., CMake, Make, or direct
compiler invocation)
■ incurs cost of processing code with static analyzer in addition to
compilation
■ static analysis can be much slower than compilation since detailed
analysis of code can incur significant computational cost
■ static analyzer can sometimes yield false positives
■ web site: https://clang-analyzer.llvm.org
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2047
Some Supported Checks
■ uninitialized arguments
■ dereferencing null pointers
■ division by zero
■ address of stack memory that escapes 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
■ new-delete mismatch
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2048
scan-build Program
■ scan-build program allows user to run static analyzer over codebase as
part of performing regular build
■ during project build, as source files compiled, also processed by static
analyzer
■ upon completion of build, results can be viewed in web browser
■ scan-build is used to invoke command that builds code (typically
CMake or Make)
■ only files that are compiled are analyzed
■ command line has following form:
scan-build [options] command [command_options]
■ some commonly-used options include:
Option Description
-h print help information and exit
-v increase verbosity of output
-o target dir set output directory to target dir
-V view analysis results in web browser
--force-analyze-debug-code force assertions to be enabled
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2049
scan-build Program (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2050
Mechanics of scan-build Program
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2051
Using scan-build With CMake
■ must run CMake configure operation and build operation with scan-build
■ all should work fine as long as CMAKE_CXX_COMPILER not set by
CMakeLists file or on command line
■ for example:
scan-build cmake -S$SOURCE_DIR -B$BINARY_DIR
scan-build cmake --build $BINARY_DIR
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2052
Uninitialized Value: Code
1 #include <iostream>
2
3 void func(int i) {
4 for (int j; j < i; ++j) {
5 std::cout << "hello\n";
6 }
7 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2053
Uninitialized Value: Static Analyzer Output
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2054
New-Delete Mismatch: Code
1 void func() {
2 int *ip = new int[1024];
3 // ...
4 delete ip;
5 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2055
New-Delete Mismatch: Static Analyzer Output
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2056
Section 7.3.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2057
Talks I
1 Gabor Horvath. Make Friends with the Clang Static Analysis Tools.
CppCon, Bellevue, WA, USA, Sept. 18–23, 2016. Available online at
https://youtu.be/AQF6hjLKsnM.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2058
Section 7.4
Clang-Tidy
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2059
Clang-Tidy
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2060
The clang-tidy Command
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2061
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–2019 Michael D. Adams C++ Version: 2019-02-04 2062
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–2019 Michael D. Adams C++ Version: 2019-02-04 2063
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–2019 Michael D. Adams C++ Version: 2019-02-04 2064
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–2019 Michael D. Adams C++ Version: 2019-02-04 2065
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–2019 Michael D. Adams C++ Version: 2019-02-04 2066
Clang Tidy With CMake
2 app.cpp
3 lib.cpp
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2067
Clang Tidy CMake Example: CMakeLists File
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.6 FATAL_ERROR)
2 project(clang_tidy LANGUAGES CXX)
3
4 find_program(CLANG_TIDY_PROGRAM NAMES "clang-tidy"
5 DOC "Path to clang-tidy executable")
6 if (CLANG_TIDY_PROGRAM)
7 set(CLANG_TIDY_OPTIONS "-warnings-as-errors=*")
8 set(RUN_CLANG_TIDY "${CLANG_TIDY_PROGRAM}" "${CLANG_TIDY_OPTIONS}")
9 endif()
10
11 add_library(lib lib.cpp)
12 add_executable(app app.cpp lib)
13 set(targets lib app)
14
15 if (CLANG_TIDY_PROGRAM)
16 set_target_properties(${targets} PROPERTIES CXX_CLANG_TIDY
17 "${RUN_CLANG_TIDY}")
18 endif()
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2068
Clang Tidy CMake Example: Source Code
lib.cpp
1 int func() {}
2
3 char* foobar() {
4 char c;
5 return &c;
6 }
app.cpp
1 int func();
2 char* foobar();
3
4 int main() {
5 auto x = 1 / 2;
6 double y = 1 / x;
7 char* cp = new char[1024];
8 delete cp;
9 func();
10 foobar();
11 int i;
12 return i + 1;
13 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2069
Section 7.4.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2070
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–2019 Michael D. Adams C++ Version: 2019-02-04 2071
Section 7.5
Valgrind
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2072
Valgrind
■ Valgrind is software framework that provides number of tools for dynamic
analysis
■ tools provided by Valgrind include:
2 Memcheck
. . . . . . . . . . . . (memory error detector)
2 Cachegrind (cache and branch-prediction profiler)
2 Callgrind
. . . . . . . . . (call-graph generating cache and branch-prediction profiler)
2 Helgrind
. . . . . . . . . (thread error detector)
2 DRD (thread error detector)
2 Massif
. . . . . . . (heap profiler)
2 DHAT (dynamic heap analysis tool)
■ above tools can be used to:
2 detect many memory management and threading bugs
2 profile programs in detail
■ access to various Valgrind tools provided via valgrind program
■ valkyrie program provides GUI for Memcheck and Helgrind tools
■ Valgrind software is open source
■ web site: http://www.valgrind.org
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2073
Valgrind (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2074
Support for Floating-Point Arithmetic in Valgrind
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2076
Data Race Example
data_race_1_0.cpp
1 #include <thread>
2 #include <iostream>
3
4 unsigned long count = 0;
5
6 void func() {
7 for (unsigned long i = 0; i < 1’000’000; ++i) {
8 ++count;
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 << count << ’\n’;
18 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2077
Helgrind Output for Data Race Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2078
Memcheck
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2079
The valgrind Program With Memcheck
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2080
Example: Source Code
memory_leak.cpp
1 int main() {
2 char* buf = new char[1024];
3 double* dp = new double;
4 // ... (no delete or delete[])
5 }
new_delete_mismatch.cpp
1 int main() {
2 char* buf = new char[1024];
3 // ...
4 delete buf;
5 }
uninitialized_memory.cpp
1 #include <iostream>
2
3 int main() {
4 int i;
5 std::cout << i << ’\n’;
6 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2081
Memcheck Output for memory_leak Program
command:
valgrind --tool=memcheck -q --leak-check=yes ./memory_leak
output:
==15265== 8 bytes in 1 blocks are definitely lost in loss record 1 of 2
==15265== at 0x4C2E1FC: operator new(unsigned long) (vg_replace_malloc.c:334)
==15265== by 0x400611: main (memory_leak.cpp:3)
==15265==
==15265== 1,024 bytes in 1 blocks are definitely lost in loss record 2 of 2
==15265== at 0x4C2E8E9: operator new[](unsigned long) (vg_replace_malloc.c:423)
==15265== by 0x400603: main (memory_leak.cpp:2)
==15265==
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2082
Memcheck Output for new_delete_mismatch Program
command:
valgrind -q --tool=memcheck ./new_delete_mismatch
output:
==15267== Mismatched free() / delete / delete []
==15267== at 0x4C2F21A: operator delete(void*) (vg_replace_malloc.c:576)
==15267== by 0x400638: main (new_delete_mismatch.cpp:4)
==15267== Address 0x5ab4c80 is 0 bytes inside a block of size 1,024 alloc’d
==15267== at 0x4C2E8E9: operator new[](unsigned long) (vg_replace_malloc.c:423)
==15267== by 0x400623: main (new_delete_mismatch.cpp:2)
==15267==
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2083
Memcheck Output for uninitialized_memory Program
command:
valgrind -q --tool=memcheck ./uninitialized_memory
output (truncated):
==15266== Conditional jump or move depends on uninitialised value(s)
==15266== at 0x4F3B8CB: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::
ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<
char, std::char_traits<char> >, std::ios_base&, char, long) const (locale_facets.tcc:874)
==15266== by 0x4F47234: put (locale_facets.h:2371)
==15266== by 0x4F47234: std::ostream& std::ostream::_M_insert<long>(long) (ostream.tcc:73)
==15266== by 0x400798: main (uninitialized_memory.cpp:5)
==15266==
==15266== Use of uninitialised value of size 8
==15266== at 0x4F3B3DE: int std::__int_to_char<char, unsigned long>(char*, unsigned long, char const*, std
::_Ios_Fmtflags, bool) (locale_facets.tcc:803)
==15266== by 0x4F3B8F4: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::
ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<
char, std::char_traits<char> >, std::ios_base&, char, long) const (locale_facets.tcc:876)
==15266== by 0x4F47234: put (locale_facets.h:2371)
==15266== by 0x4F47234: std::ostream& std::ostream::_M_insert<long>(long) (ostream.tcc:73)
==15266== by 0x400798: main (uninitialized_memory.cpp:5)
==15266==
==15266== Conditional jump or move depends on uninitialised value(s)
==15266== at 0x4F3B3EB: int std::__int_to_char<char, unsigned long>(char*, unsigned long, char const*, std
::_Ios_Fmtflags, bool) (locale_facets.tcc:806)
==15266== by 0x4F3B8F4: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::
ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<
char, std::char_traits<char> >, std::ios_base&, char, long) const (locale_facets.tcc:876)
==15266== by 0x4F47234: put (locale_facets.h:2371)
==15266== by 0x4F47234: std::ostream& std::ostream::_M_insert<long>(long) (ostream.tcc:73)
==15266== by 0x400798: main (uninitialized_memory.cpp:5)
==15266==
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2084
Massif
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2085
The valgrind Command with Massif
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2086
Analyzing Massif Data Files
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2087
Massif Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2088
Massif Example: ms_print Output
command:
ms_print --x=70 --y=10 profile.massif
output (truncated):
--------------------------------------------------------------------------------
Command : ./ heap_1
Massif arguments : -- massif -out - file = profile . massif -- stacks = yes
ms_print arguments : --x =70 --y =10 profile . massif
--------------------------------------------------------------------------------
MB
2.071ˆ ##
| # :::::::::::::::::::::: @ :::::: @ :::
| # :: : ::::::::::::::::: @ :::::: @ :::
| # :: : ::::::::::::::::: @ :::::: @ :::
| # :: : ::::::::::::::::: @ :::::: @ :::
| @ ::: @ :::: @ :::::::# :: : ::::::::::::::::: @ :::::: @ :::
| @: :@ :::: @ ::: :: # :: : ::::::::::::::::: @ :::::: @ :::
| @: :@ :::: @ ::: :: # :: : ::::::::::::::::: @ :::::: @ :::
| @ :::::: @:@: :@ :::: @ ::: :: # :: : ::::::::::::::::: @ :::::: @ :::
| :::: @ :::: :@:@: :@ :::: @ ::: :: # :: : ::::::::::::::::: @ :::::: @ :::
0 +---------------------------------------------------------------------> Mi
0 312.7
Number of snapshots : 71
Detailed snapshots : [8 , 14 , 16 , 19 , 24 , 31 ( peak ) , 55 , 65]
--------------------------------------------------------------------------------
n time (i) total (B) useful - heap (B) extra - heap (B) stacks (B)
--------------------------------------------------------------------------------
0 0 0 0 0 0
1 5 ,653 ,795 90 ,704 90 ,112 24 568
2 9 ,989 ,271 139 ,760 139 ,264 24 472
3 15 ,104 ,837 205 ,296 204 ,800 24 472
4 19 ,380 ,693 205 ,296 204 ,800 24 472
5 25 ,946 ,699 336 ,296 335 ,872 24 400
6 30 ,435 ,811 336 ,296 335 ,872 24 400
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2089
Massif-Visualizer Example: Tool Output
command:
massif-visualizer profile.massif
output:
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2090
Section 7.5.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2091
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2092
Section 7.6
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2093
Gcov
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2094
The gcov Program
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2095
Using Gcov
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2096
Using Gcov: Practical Considerations
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2097
Signum Example: Source Code
app.cpp
1 #include <cstdlib>
2 #include <iostream>
3 #include "utility.hpp"
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 }
utility.hpp
1 int signum(double x);
signum.cpp
1 #include "utility.hpp"
2 int signum(double x) {
3 if (x > 0) {
4 return 1;
5 } else if (x < 0) {
6 return -1;
7 } else {
8 return 0;
9 }
10 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2098
Signum Example: Using Gcov
■ build program app using GCC, ensuring that --coverage option is used
for both compiling and linking; for example, use command sequence like:
g++ -O0 --coverage -c app.cpp
g++ -O0 --coverage -c signum.cpp
g++ -O0 --coverage -o app app.o signum.o
■ run app program twice as follows:
./app 0
./app 1
■ since app program run twice, statistics are accumulated from both runs of
program
■ run Gcov; for example, use command like:
gcov -b -c -m app.o signum.o
■ view resulting .gcov files (examples of which are given on following slides)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2099
Signum Example: Gcov Output [app.cpp.gcov]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2100
Signum Example: Gcov Output [signum.cpp.gcov]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2101
LLVM Cov with GCC-Style Coverage
■ LLVM Cov is code coverage analysis tool, which is part of LLVM software,
intended for use with Clang compiler
■ supports measurement of function, statement, and decision (i.e., branch)
coverage
■ manner in which coverage information collected similar to case of Gcov
■ LLVM Cov provided as program called llvm-cov
■ coverage data files produced by compiler and program execution
processed with gcov subcommand of llvm-cov
■ gcov subcommand of llvm-cov has very similar interface as gcov
program (of Gcov)
■ web site: http://llvm.org/docs/CommandGuide/llvm-cov.html
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2102
Signum Example (Revisited): Using LLVM Cov
■ build program app using Clang, ensuring that --coverage option is used
for both compiling and linking; for example, use command sequence like:
clang++ -O0 --coverage -c app.cpp
clang++ -O0 --coverage -c signum.cpp
clang++ -O0 --coverage -o app app.o signum.o
■ run app program twice as follows:
./app 0
./app 1
■ since app program run twice, statistics are accumulated from both runs of
program
■ run LLVM Cov; for example, use command like:
llvm-cov gcov -b -c app.o signum.o
■ view resulting .gcov files (which look similar to ones produced by Gcov;
see earlier Gcov example)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2103
Lcov
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2104
The lcov Program: Capture
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2105
The lcov Program: Capture (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2106
The lcov Program: Summary
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2107
The genhtml Program
■ coverage report can be generated in HTML format from one or more trace
files with genhtml program, which has following command-line interface:
genhtml [options] $trace_file...
■ some commonly-used options include:
Option Description
--legend include legend in report
--branch-coverage include branch coverage information in re-
port
-o dir set output directory for HTML document to
dir
-h print help information and exit
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2108
Using Lcov
1 build project with GCC or Clang, ensuring that --coverage option is used
for compiling and linking (as in earlier Gcov example)
2 run program (one or more times) to collect coverage data (e.g., as in
earlier Gcov or LLVM Cov examples)
3 process coverage data with Lcov; for example, use command like
following (where coverage data files under current directory):
lcov -c --gcov-tool $gcov_tool -d . -o coverage.lcov \
--rc lcov_branch_coverage=1
where $gcov_tool is gcov for GCC or pathname of wrapper
. . . . . . . . . script
. . . . . . shown
earlier for Clang
4 if HTML output desired, generate output using genhtml; for example, use
command like following:
genhtml --branch-coverage --legend -o output coverage.lcov
(main HTML document accessible via output/index.html)
5 if plaintext output desired, brief summary can be generated from tracefile
using command like:
lcov --summary coverage.lcov --rc lcov_branch_coverage=1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2109
Signum Example (Revisited): Lcov Summary Output
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2110
Signum Example (Revisited): Lcov HTML Output [Summary]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2111
Signum Example (Revisited): Lcov HTML Output [app.cpp]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2112
Signum Example (Revisited): Lcov Output [signum.cpp]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2113
Lcov Caveats
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2114
Templates Example: Source Code
main.cpp
1 #include "utility.hpp"
2
3 int main() {
4 Widget<int> wi(42);
5 wi.get();
6 Widget<double> wd(42.0);
7 wd.get();
8 abs(42);
9 abs(42.0);
10 }
utility.hpp
1 template <class T> class Widget {
2 public:
3 Widget (T x) : x_(x) {}
4 T get() const {
5 return x_;
6 }
7 private:
8 T x_;
9 };
10
11 template <class T> T abs(T x) {
12 if (x < 0) {
13 return -x;
14 } else {
15 return x;
16 }
17 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2115
Templates Example: Gcov Output [main.cpp.gcov]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2116
Templates Example: Gcov Output [utility.hpp.gcov]
-: 0: Source : utility . hpp
-: 0: Graph : main . gcno
-: 0: Data : main . gcda
-: 0: Runs :1
-: 0: Programs :1
-: 1: template < class T > class Widget {
-: 2: public :
2: 3: Widget (T x) : x_ (x) {}
------------------
Widget < double >:: Widget ( double ):
function Widget < double >:: Widget ( double ) called 1 returned 100% blocks executed 100%
1: 3: Widget (T x) : x_ (x) {}
------------------
Widget <int >:: Widget ( int ):
function Widget <int >:: Widget ( int ) called 1 returned 100% blocks executed 100%
1: 3: Widget (T x) : x_ (x) {}
------------------
2: 4: T get () const {
2: 5: return x_ ;
-: 6: }
------------------
Widget < double >:: get () const :
function Widget < double >:: get () const called 1 returned 100% blocks executed 100%
1: 4: T get () const {
1: 5: return x_ ;
-: 6: }
------------------
Widget <int >:: get () const :
function Widget <int >:: get () const called 1 returned 100% blocks executed 100%
1: 4: T get () const {
1: 5: return x_ ;
-: 6: }
------------------
-: 7: private :
-: 8: T x_ ;
-: 9:};
-: 10:
2: 11: template < class T > T abs (T x) {
2: 12: if (x < 0) {
#####: 13: return -x;
-: 14: } else {
2: 15: return x;
-: 16: }
-: 17:}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2117
Templates Example: Lcov Output [main.cpp]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2118
Templates Example: Lcov Output [utility.hpp]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2119
Exceptions Example: Source Code
main.cpp
1 #include <vector>
2 #include "utility.hpp"
3
4 void increment_3(int& i) noexcept {++i;}
5 void increment_4(int& i) {++i;}
6
7 int main() {
8 std::vector<int> u;
9 std::vector<int> v{1, 2, 3, 4};
10 u = v;
11 increment_1(u[0]);
12 increment_2(u[0]);
13 increment_3(u[0]);
14 increment_4(u[0]);
15 }
utility.hpp
1 void increment_1(int&) noexcept;
2 void increment_2(int&);
utility.cpp
1 #include "utility.hpp"
2
3 void increment_1(int& i) noexcept {++i;}
4 void increment_2(int& i) {++i;}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2120
Exceptions Example: Gcov Output [main.cpp.gcov]
-: 0: Source : main . cpp
-: 0: Programs :2
-: 1:# include <vector >
-: 2:# include " utility . hpp "
-: 3:
1: 4: void increment_3 ( int & i) noexcept {++ i ;}
------------------
increment_3 ( int &):
function increment_3 ( int &) called 1 returned 100% blocks executed 100%
------------------
increment_3 ( int &):
function increment_3 ( int &) called 1 returned 100% blocks executed 100%
------------------
1: 5: void increment_4 ( int & i) {++ i ;}
------------------
increment_4 ( int &):
function increment_4 ( int &) called 1 returned 100% blocks executed 100%
------------------
increment_4 ( int &):
function increment_4 ( int &) called 1 returned 100% blocks executed 100%
------------------
-: 6:
1: 7: int main () {
2: 8: std :: vector <int > u;
call 0 returned 1
call 1 returned 1
call 2 never executed
2: 9: std :: vector <int > v{1 , 2, 3, 4};
call 0 returned 1
call 1 returned 1
branch 2 taken 1 ( fallthrough )
branch 3 taken 0 ( throw )
call 4 returned 1
call 5 never executed
1: 10: u = v;
call 0 returned 1
branch 1 taken 1 ( fallthrough )
branch 2 taken 0 ( throw )
1: 11: increment_1 (u [0]);
call 0 returned 1
call 1 returned 1
1: 12: increment_2 (u [0]);
call 0 returned 1
call 1 returned 1
branch 2 taken 1 ( fallthrough )
branch 3 taken 0 ( throw )
1: 13: increment_3 (u [0]);
call 0 returned 1
call 1 returned 1
1: 14: increment_4 (u [0]);
call 0 returned 1
call 1 returned 1
1: 15:}
------------------
main :
function main called 1 returned 100% blocks executed 72%
------------------
main :
function main called 1 returned 100% blocks executed 72%
------------------
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2121
Exceptions Example: Gcov Output [utility.cpp.gcov]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2122
Exceptions Example: Lcov Output [Summary]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2123
Exceptions Example: Lcov Output [main.cpp]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2124
Exceptions Example: Lcov Output [utility.cpp]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2125
Short-Circuiting Example: Source Code
utility.hpp
1 class Flag {
2 public:
3 explicit Flag(bool b = false) noexcept : b_(b) {}
4 explicit operator bool() const noexcept {return b_;}
5 Flag operator&&(const Flag& other) noexcept {
6 return Flag(b_ && other.b_);
7 }
8 Flag operator||(const Flag& other) noexcept {
9 return Flag(b_ || other.b_);
10 }
11 private:
12 bool b_;
13 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2126
Short-Circuiting Example: Source Code
main.cpp
1 #include <tuple>
2 #include "utility.hpp"
3
4 bool func(bool a, bool b, bool c) noexcept {
5 return (a || b) && c;
6 }
7
8 Flag func(Flag a, Flag b, Flag c) noexcept {
9 return (a || b) && c;
10 }
11
12 int main() {
13 std::tuple<bool, bool, bool> tests[] = {{0, 0, 0}, {1, 1, 1}};
14 for (auto&& [a, b, c] : tests) {
15 func(a, b, c);
16 func(Flag(a), Flag(b), Flag(c));
17 }
18 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2127
Short-Circuiting Example: Lcov Output [main.cpp]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2128
Short-Circuiting Example: Lcov Output [utility.hpp]
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2129
Virtual Destructor Example 1A: Source Code
example_1_1.cpp
1 struct Gadget {
2 Gadget();
3 virtual ˜Gadget();
4 };
5 Gadget::Gadget() {}
6 Gadget::˜Gadget() {}
7
8 struct Widget : Gadget {
9 Widget();
10 virtual ˜Widget();
11 };
12 Widget::Widget() {}
13 Widget::˜Widget() {}
14
15 int main() {
16 Widget w;
17 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2130
Virtual Destructor Example 1A: Lcov Output
example_1_2.cpp
1 struct Gadget {
2 Gadget();
3 virtual ˜Gadget();
4 };
5 Gadget::Gadget() {}
6 Gadget::˜Gadget() {}
7
8 struct Widget : Gadget {
9 Widget();
10 virtual ˜Widget();
11 };
12 Widget::Widget() {}
13 Widget::˜Widget() {}
14
15 int main() {
16 Widget w;
17 Widget* wp = new Widget;
18 delete wp;
19 // invoke deleting destructor (Itanium C++ ABI)
20 Gadget* gp = new Gadget;
21 delete gp;
22 // invoke deleting destructor (Itanium C++ ABI)
23 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2132
Virtual Destructor Example 1B: Lcov Output
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2133
Gcov and Lcov with CMake
CodeCoverage.cmake
■ slightly modified version of above module can be found at:
2 https://github.com/mdadams/cmake-modules/blob/master/
CodeCoverage.cmake
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2134
Gcov/Lcov CMake Example: CMakeLists File
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
2 project(coverage_example LANGUAGES CXX C)
3 option(ENABLE_COVERAGE "Enable coverage" false)
4 set(CMAKE_VERBOSE_MAKEFILE true)
5 set(CMAKE_EXPORT_COMPILE_COMMANDS true)
6
7 if (ENABLE_COVERAGE)
8 set(CMAKE_BUILD_TYPE "Debug" CACHE STRING
9 "Set the build type." FORCE)
10 include(CodeCoverage.cmake)
11 APPEND_COVERAGE_COMPILER_FLAGS()
12 endif()
13
14 add_executable(app app.cpp)
15
16 if (ENABLE_COVERAGE)
17 setup_target_for_coverage_lcov(
18 NAME coverage
19 EXECUTABLE ${CMAKE_CURRENT_SOURCE_DIR}/run_tests
20 EXECUTABLE_ARGS ${CMAKE_CURRENT_BINARY_DIR}
21 LCOV_ARGS -rc lcov_branch_coverage=1
22 GENHTML_ARGS --branch-coverage
23 DEPENDENCIES app)
24 endif()
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2135
Gcov/Lcov CMake Example: Source File and Test Script
app.cpp
1 #include <iostream>
2
3 void func(int x) {
4 if (x % 2) {
5 std::cout << "odd\n";
6 } else {
7 std::cout << "even\n";
8 }
9 }
10
11 int main() {
12 for (int i = 0; i < 100; ++i) {
13 func(i);
14 }
15 }
run_tests
1 #! /usr/bin/env bash
2
3 BINARY_DIR="$1"
4
5 $BINARY_DIR/app
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2136
Gcovr
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2137
Section 7.6.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2138
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2139
Section 7.7
LLVM XRay
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2140
LLVM XRay
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2141
Instrumenting Application
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2143
XRay Runtime Library
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2145
LLVM XRay Tool
2 performs basic function call accounting statistics with various options for
sorting and output formats
2 convert
2 converts XRay log file from one format to another
2 extract
2 extracts instrumentation map from binary and returns as YAML
2 graph
2 generates DOT graph of function call relationships between functions in XRay
trace
2 stack
2 reconstructs function call stacks from timeline of function calls in XRay trace
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2146
Code Example
example_1.cpp
1 #include <iostream>
2 #include <thread>
3 #include <chrono>
4
5 [[gnu::noinline, clang::xray_always_instrument]]
6 void delay() {
7 using namespace std::chrono_literals;
8 std::this_thread::sleep_for(1ms);
9 }
10
11 [[gnu::noinline, clang::xray_always_instrument,
12 clang::xray_log_args(1)]]
13 void func(int i, std::size_t n) {
14 for (std::size_t j = 0; j < n; ++j) {
15 std::cout << (i ? ’X’ : ’O’) << std::flush;
16 delay();
17 }
18 }
19
20 [[clang::xray_never_instrument]]
21 int main() {
22 std::thread t1([](){func(0, 500);});
23 std::thread t2([](){func(1, 500);});
24 t1.join(); t2.join();
25 std::cout << ’\n’;
26 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2147
Instrument Code and Run Application
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2148
Account Subcommand Example
command:
llvm-xray account -k -m ./example_1 xray-log.example_1.5fNAQv
output produced:
Functions with latencies: 3
funcid count [ min, med, 90p, 99p, max] sum function
1 1000 [ 0.000782, 0.000827, 0.000835, 0.000899, 0.000919] 0.825670 <invalid>:0:0: delay()
2 1 [ 0.420710, 0.420710, 0.420710, 0.420710, 0.420710] 0.420710 <invalid>:0:0: func(int,
unsigned long)
3 1000 [ 0.000781, 0.000825, 0.000831, 0.000896, 0.000917] 0.822853 <invalid>:0:0: void std::
this_thread::sleep_for<long, std::ratio<1l, 1000l> >(std::chrono::duration<long, std::ratio<1
l, 1000l> > const&)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2149
Stack Subcommand Example
command:
llvm-xray stack -m ./example_1 xray-log.example_1.5fNAQv
output produced:
Unique Stacks: 2
Top 10 Stacks by leaf sum:
Sum: 1439155104
lvl function count sum
#0 func(int, unsigned long) 1 1475472614
#1 delay() 500 1446943616
#2 void std::this_thread::sleep_for<long, std::ratio<1l, 100... 499 1439155104
Sum: 1437712794
lvl function count sum
#0 func(int, unsigned long) 1 1472486054
#1 delay() 500 1442899912
#2 void std::this_thread::sleep_for<long, std::ratio<1l, 100... 500 1437712794
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2150
Trace Subcommand Example
command:
llvm-xray convert -f yaml -y -m ./example_1 xray-log.example_1.5fNAQv
output produced:
---
header:
version: 3
type: 0
constant-tsc: true
nonstop-tsc: true
cycle-frequency: 3500000000
records:
- { type: 0, func-id: 2, function: ’func(int, unsigned long)’, args: [ 0 ], cpu: 5, thread: 29992,
process: 29991, kind: function-enter-arg, tsc: 1027021087397834 }
- { type: 0, func-id: 2, function: ’func(int, unsigned long)’, args: [ 1 ], cpu: 4, thread: 29993,
process: 29991, kind: function-enter-arg, tsc: 1027021087460270 }
- { type: 0, func-id: 1, function: ’delay()’, cpu: 5, thread: 29992, process: 29991, kind: function-enter
, tsc: 1027021087525002 }
- { type: 0, func-id: 3, function: ’void std::this_thread::sleep_for<long, std::ratio<1l, 1000l> >(std::
chrono::duration<long, std::ratio<1l, 1000l> > const&)’, cpu: 5, thread: 29992, process: 29991,
kind: function-enter, tsc: 1027021087526060 }
- { type: 0, func-id: 1, function: ’delay()’, cpu: 4, thread: 29993, process: 29991, kind: function-enter
, tsc: 1027021087532070 }
- { type: 0, func-id: 3, function: ’void std::this_thread::sleep_for<long, std::ratio<1l, 1000l> >(std::
chrono::duration<long, std::ratio<1l, 1000l> > const&)’, cpu: 4, thread: 29993, process: 29991,
kind: function-enter, tsc: 1027021087533716 }
- { type: 0, func-id: 3, function: ’void std::this_thread::sleep_for<long, std::ratio<1l, 1000l> >(std::
chrono::duration<long, std::ratio<1l, 1000l> > const&)’, cpu: 4, thread: 29993, process: 29991,
kind: function-exit, tsc: 1027021090288388 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2151
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2152
Section 7.8
Miscellaneous Tools
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2153
Kcov
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2154
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2155
Section 7.9
Catch2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2156
Catch2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2157
Counter Class Example: counter.hpp
1 #include <limits>
2 #include <stdexcept>
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 }
10 counter(count_type count = 0) : count_(count) {}
11 count_type get_count() const {
12 return count_;
13 }
14 void increment() {
15 if (count_ == max_count()) {
16 throw std::overflow_error("counter overflow");
17 }
18 ++count_;
19 }
20 private:
21 count_type count_;
22 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2158
Counter Class Example: Test Code
1 #define CATCH_CONFIG_MAIN
2 #include <catch2/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–2019 Michael D. Adams C++ Version: 2019-02-04 2159
Section Example
1 #define CATCH_CONFIG_MAIN
2 #include <catch2/catch.hpp>
3 #include <cstddef>
4 #include <vector>
5
6 TEST_CASE("Check resize", "[vector]") {
7 constexpr std::size_t size = 128;
8 std::vector<int> x(size);
9 REQUIRE(x.size() == size);
10 REQUIRE(x.capacity() >= size);
11 SECTION("Increase size") {
12 std::size_t n = size * 16;
13 x.resize(n);
14 CHECK(x.size() == n);
15 CHECK(x.capacity() >= n);
16 }
17 SECTION("Decrease size") {
18 std::size_t n = size / 16;
19 x.resize(n);
20 CHECK(x.size() == n);
21 CHECK(x.capacity() >= n);
22 }
23 SECTION("Zero size") {
24 x.resize(0);
25 CHECK(x.size() == 0);
26 CHECK(x.capacity() >= size);
27 }
28 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2160
Approximate Comparison Example
1 #define CATCH_CONFIG_MAIN
2 #include <catch2/catch.hpp>
3
4 TEST_CASE("addition") {
5 float x = 0.0f;
6 for (int i = 0; i < 10; ++i) {x += 0.1f;}
7 CHECK_NOFAIL(x == 1.0f);
8 // condition may be false due to roundoff error
9 CHECK(x == Approx(1.0f));
10 // should pass
11 CHECK(x == Approx(1.0f).margin(0.01f));
12 // should pass (absolute tolerance 0.01)
13 CHECK(x == Approx(1.0f).epsilon(0.01f));
14 // should pass (relative tolerance 1%)
15 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2161
Type-Parameterized Test Example: stack.hpp
1 #include <cstddef>
2 #include <vector>
3
4 // Note: T is not allowed to be bool.
5 template <class T>
6 class Stack {
7 public:
8 bool empty() const {return s_.empty();}
9 std::size_t size() const {return s_.size();}
10 const T& top() const {return s_.back();}
11 void push(const T& x) {s_.push_back(x);}
12 void pop() {s_.pop_back();}
13 private:
14 std::vector<T> s_;
15 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2162
Type-Parameterized Test Example: Test Code
1 #define CATCH_CONFIG_MAIN
2 #include <catch2/catch.hpp>
3 #include <complex>
4 #include "stack.hpp"
5
6 TEMPLATE_TEST_CASE("Check default constructor", "[constructors]",
7 int, double, std::complex<double>) {
8 Stack<TestType> s;
9 CHECK(s.empty());
10 CHECK(s.size() == 0);
11 }
12
13 TEMPLATE_TEST_CASE("Check push and pop", "[modifiers]",
14 int, double, std::complex<double>) {
15 std::size_t size = 0;
16 Stack<TestType> s;
17 REQUIRE(s.size() == size);
18 while (size < 3) {
19 ++size; s.push(TestType(size));
20 REQUIRE(s.size() == size);
21 REQUIRE(s.top() == TestType(size));
22 }
23 while (size > 0) {
24 s.pop(); --size;
25 REQUIRE(s.size() == size);
26 }
27 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2163
Test Fixture Example
1 #define CATCH_CONFIG_MAIN
2 #include <catch2/catch.hpp>
3 #include <deque>
4 #include <stdexcept>
5
6 class TestFixture {
7 public:
8 TestFixture() : q0(), q1{1, 2, 3} {
9 }
10 protected:
11 std::deque<int> q0;
12 std::deque<int> q1;
13 };
14
15 TEST_CASE_METHOD(TestFixture, "Check deque at", "[deque]") {
16 CHECK_THROWS_AS(q0.at(0), std::out_of_range);
17 CHECK(q1.at(0) == 1);
18 CHECK_THROWS_AS(q1.at(3), std::out_of_range);
19 }
20
21 TEST_CASE_METHOD(TestFixture, "Check deque clear", "[deque]") {
22 q0.clear();
23 CHECK(q0.size() == 0);
24 q1.clear();
25 CHECK(q1.size() == 0);
26 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2164
Talks I
1 Phil Nash. Modern C++ Testing with Catch2. CppCon, Bellevue, WA,
USA, Sept. 24, 2018. Available online at
https://youtu.be/Ob5_XZrFQH0.
2 Phil Nash. Modern C++ Testing with Catch2. Meeting C++, Berlin,
Germany, Nov. 9, 2017. Available online at
https://youtu.be/3tIE6X5FjDE.
3 Phil Nash. Modern C++ Testing with Catch2. C++ Edinburgh, Edinburgh,
UK, Aug. 14, 2017. Available online at
https://youtu.be/grC0S6ZK59U.
4 Phil Nash. Test Driven C++ with Catch. CppCon, Bellevue, WA, USA,
Sept. 22, 2015. Available online at https://youtu.be/gdzP3pAC6UI.
5 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–2019 Michael D. Adams C++ Version: 2019-02-04 2165
Part 8
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2166
Section 8.1
Perf
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2167
Linux Kernel Perf Event Interface
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2168
Perf
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2169
Events
■ hardware event:
2 event measurable by performance monitoring unit (PMU) of processor
2 examples: CPU cycles (cycles) and cache misses (cache-misses)
■ hardware cache event:
2 event measurable by PMU of processor
2 examples: L1 data cache load misses (L1-dcache-load-misses) and
data translation-lookaside-buffer load misses (dTLB-load-misses)
■ software event:
2 low-level events based on kernel counters
2 examples: CPU clock (cpu-clock) and page fault (page-faults)
■ kernel tracepoint event:
2 predefined static instrumentation points in kernel code where trace
information can be collected
2 examples: entering open system call (syscalls:sys_enter_open) and
context switch (sched:sched_switch)
■ probe event:
2 user-defined events dynamically inserted into kernel
2 created using uprobes or kprobes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2170
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–2019 Michael D. Adams C++ Version: 2019-02-04 2171
Stack Traces
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2172
Event-Based Sampling
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2174
Event Specifiers (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2175
Hardware Event Skid
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2176
The perf Program
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2177
Perf List Command
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2178
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–2019 Michael D. Adams C++ Version: 2019-02-04 2179
Perf Stat Command
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2180
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–2019 Michael D. Adams C++ Version: 2019-02-04 2181
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–2019 Michael D. Adams C++ Version: 2019-02-04 2182
Perf Record Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2183
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–2019 Michael D. Adams C++ Version: 2019-02-04 2184
Perf Report Example
#
# ( For a higher level overview , try : perf report -- sort comm , dso )
#
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2185
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–2019 Michael D. Adams C++ Version: 2019-02-04 2186
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–2019 Michael D. Adams C++ Version: 2019-02-04 2187
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–2019 Michael D. Adams C++ Version: 2019-02-04 2188
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–2019 Michael D. Adams C++ Version: 2019-02-04 2189
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–2019 Michael D. Adams C++ Version: 2019-02-04 2190
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–2019 Michael D. Adams C++ Version: 2019-02-04 2191
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–2019 Michael D. Adams C++ Version: 2019-02-04 2192
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–2019 Michael D. Adams C++ Version: 2019-02-04 2193
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–2019 Michael D. Adams C++ Version: 2019-02-04 2194
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–2019 Michael D. Adams C++ Version: 2019-02-04 2195
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–2019 Michael D. Adams C++ Version: 2019-02-04 2196
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–2019 Michael D. Adams C++ Version: 2019-02-04 2197
Additional Remarks
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2198
Flame Graphs
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2199
Flame Graph Example
Flame Graph
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2200
Generating Flame Graphs
■ can generate flamegraphs from Perf data by using software available from
2 https://github.com/brendangregg/FlameGraph
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2201
Section 8.1.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2202
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2203
References I
1 B. Gregg, The Flame Graph. ACM Queue, March 2016, pages 1–28.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2204
Section 8.2
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2205
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:
2 cache behavior
2 memory and resource contention
2 floating-point efficiency
2 branch behavior
■ often, processor itself in best position to provide information related to
above factors
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2206
Hardware Performance Counters
■ hardware performance counters are specialized registers used to
measure various aspects of processor performance
■ hardware counters can provide insight into:
2 timing
2 cache behaviors (e.g., cache misses and cache coherence protocol events)
2 branch behaviors (e.g., incorrect branch predictions)
2 pipeline behavior (e.g., stalls)
2 memory and resource access patterns
2 floating-point efficiency
2 instructions per cycle
■ hardware counter information can be obtained with:
2 subroutine or basic block resolution
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 2207
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–2019 Michael D. Adams C++ Version: 2019-02-04 2208
Events
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2209
Events (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2210
PAPI
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2211
PAPI High-Level Interface
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2212
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–2019 Michael D. Adams C++ Version: 2019-02-04 2213
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–2019 Michael D. Adams C++ Version: 2019-02-04 2214
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–2019 Michael D. Adams C++ Version: 2019-02-04 2215
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–2019 Michael D. Adams C++ Version: 2019-02-04 2216
Some Commonly-Used Preset Events (Continued 3)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2217
Event Usage Examples
■ most frequently used events are often those related to cache behavior
■ instructions per cycle could be computed from events:
2 PAPI_TOT_CYC and PAPI_TOT_INS
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2218
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–2019 Michael D. Adams C++ Version: 2019-02-04 2219
PAPI Low-Level Interface
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2220
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–2019 Michael D. Adams C++ Version: 2019-02-04 2221
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–2019 Michael D. Adams C++ Version: 2019-02-04 2222
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–2019 Michael D. Adams C++ Version: 2019-02-04 2223
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
--------------------------------------------------------------------------------
avail .c PASSED
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2224
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
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
mem_info .c PASSED
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2225
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–2019 Michael D. Adams C++ Version: 2019-02-04 2226
Section 8.2.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2227
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2228
Section 8.3
Gprof
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2229
Gprof
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2230
Comments on Gprof
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2232
Using Gprof
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2233
Gprof Output
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2234
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–2019 Michael D. Adams C++ Version: 2019-02-04 2235
Example: Source Code (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2236
Flat Profile Information
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2237
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–2019 Michael D. Adams C++ Version: 2019-02-04 2238
Call Graph Information
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2239
Call Graph Information (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2240
Example: Call Graph
Call graph
granularity : each sample hit covers 2 byte (s) for 0.12% of 8.51 seconds
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2241
Section 8.3.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2242
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2243
Section 8.4
Valgrind/Callgrind
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2244
Callgrind
■ Callgrind is Valgrind
. . . . . . . . tool that 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)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2245
The valgrind Command with Callgrind Tool
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2246
The callgrind_annotate Command
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2247
Using Callgrind
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2248
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–2019 Michael D. Adams C++ Version: 2019-02-04 2249
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–2019 Michael D. Adams C++ Version: 2019-02-04 2250
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–2019 Michael D. Adams C++ Version: 2019-02-04 2251
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–2019 Michael D. Adams C++ Version: 2019-02-04 2252
KCachegrind
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2253
KCachegrind Example: Source/Assembly Annotation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2254
KCachegrind Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2255
Section 8.4.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2256
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2257
Part 9
Build Tools
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2258
Section 9.1
Build Tools
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2259
Build Tools
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2260
Examples of Build Tools
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2261
Section 9.2
Make
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2262
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–2019 Michael D. Adams C++ Version: 2019-02-04 2263
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–2019 Michael D. Adams C++ Version: 2019-02-04 2264
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–2019 Michael D. Adams C++ Version: 2019-02-04 2265
Makefiles (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2266
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–2019 Michael D. Adams C++ Version: 2019-02-04 2267
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–2019 Michael D. Adams C++ Version: 2019-02-04 2268
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–2019 Michael D. Adams C++ Version: 2019-02-04 2269
Section 9.2.1
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2270
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2271
Section 9.3
CMake
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2272
CMake
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2273
Users of CMake
■ CMake has very large user base and is employed in many open-source
and commercial projects
■ some users of CMake include:
2 Blender (https://www.blender.org)
2 Clang (http://clang.llvm.org)
2 Computational Geometry Algorithms Library (CGAL)
(http://www.cgal.org)
2 JasPer Image Processing/Coding Tool Kit
(http://www.ece.uvic.ca/˜mdadams/jasper)
2 K Desktop Environment (KDE) (https://www.kde.org)
2 MySQL (https://www.mysql.com)
2 Netflix (https://www.netflix.com)
2 OpenCV (http://opencv.org)
2 Qt (https://www.qt.io)
2 Second Life (http://secondlife.com)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2274
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–2019 Michael D. Adams C++ Version: 2019-02-04 2275
CMake Basics
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2278
The cmake Command for Configuring (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2279
The cmake Command for Building
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2280
Hello World Example
■ source directory $SOURCE_DIR contains two files:
1 CMakeLists.txt
2 hello.cpp
$SOURCE_DIR/CMakeLists.txt
1 # Specify minimum required version of CMake.
2 cmake_minimum_required(VERSION 3.1 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–2019 Michael D. Adams C++ Version: 2019-02-04 2281
Section 9.3.1
CMakeLists Files
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2282
Projects, Targets, and Build Configurations
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2283
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–2019 Michael D. Adams C++ Version: 2019-02-04 2284
Variables
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2285
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:
2 find
2 system introspection
2 utility
■ find module:
2 determines location of software elements such as header files and libraries
2 often module name starts with prefix “Find”
2 examples: FindBoost and FindOpenGL
■ system introspection module:
2 provides information about target system or compiler (e.g., size of various
types, availability of header files, compiler version)
2 often module name starts with prefix “Test” or “Check”
2 examples: CheckCXXSourceCompiles and CheckIncludeFile
■ utility module:
2 provides additional functions/macros for convenience
2 example: ExternalProject
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2286
Modules (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2287
Commonly-Used Variables
■ Source and binary directories:
2 CMAKE_BINARY_DIR. The full path to the top-level directory of the current
■ C++ compiler:
2 CMAKE_CXX_COMPILER_ID. The C++ compiler in use (e.g., Clang, GNU,
Intel, MSVC).
2 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).
2 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.
2 CMAKE_CXX_COMPILER. The compiler command used for C++ source code.
2 CMAKE_CXX_FLAGS. The compiler flags for compiling C++ source code.
2 CMAKE_CXX_FLAGS_DEBUG. The compiler flags for compiling C++ source
code for a debug build.
2 CMAKE_CXX_FLAGS_RELEASE. The compiler flags for compiling C++ source
code for a release build.
2 CMAKE_CXX_FLAGS_RELWITHDEBINFO. The compiler flags for compiling
C++ source code for a release build with debug flags.
2 CMAKE_CXX_FLAGS_MINSIZEREL. The compiler flags for compiling C++
source code for a release build with minimum code size.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2289
Commonly-Used Variables (Continued 2)
■ Linker:
2 CMAKE_EXE_LINKER_FLAGS. The linker flags used to create executables.
Windows, Darwin).
2 UNIX. Specifies if the target system’s OS is UNIX (or UNIX-like).
2 APPLE. Specifies if the target system’s OS is Mac OS X.
2 WIN32. Specifies if the target system’s OS is Microsoft Windows (32- or
64-bit).
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2290
Commonly-Used Variables (Continued 3)
■ Makefile builds:
2 CMAKE_VERBOSE_MAKEFILE. Enable/disable verbose output from Makefile
builds.
2 CMAKE_RULE_MESSAGES. Specify if a progress message should be reported
by each makefile rule.
■ Other:
2 CMAKE_MODULE_PATH. The list of directories to search for CMake modules.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2291
Commonly-Used Commands
■ Initialization:
2 cmake_minimum_required. Set the minimum required version of Cmake
for a project.
2 cmake_policy. Manage CMake policy settings. (This is used to select
between old and new behaviors in CMake.)
2 project. Set a name, version, and enable languages for the entire project.
(If no languages specified, defaults to C and C++.)
2 option. Provide an option that the user can (optionally) select.
■ Adding targets:
2 add_executable. Add a program target.
2 add_library. Add a library target.
2 add_test. Add a test target. (This is used in conjunction with the module
CTest.)
2 add_custom_target. Add a target with no output file that is always out of
date.
2 add_custom_command. Add a custom build rule to the generated build
system.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2292
Commonly-Used Commands (Continued 1)
for linking a target. (May be used multiple times for the same target.)
2 set_target_properties. Set properties for a target. (Some properties
include: OUTPUT_NAME, SOVERSION, and VERSION.)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2293
Commonly-Used Commands (Continued 2)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2294
Commonly-Used Commands (Continued 3)
in a list.
2 while and endwhile. Evaluate a group of commands while a condition is
true.
2 function and endfunction. Record a function for later invocation as a
command.
2 macro and endmacro. Record a macro for later invocation as a command.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2295
Commonly-Used Commands (Continued 4)
■ Other:
2 message. Display a message to the user.
2 configure_file. Copy a file to another location and modify its contents.
2 install. Specify rules to run at install time (e.g., rules to install programs,
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2296
Commonly-Used Modules
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2297
Commonly-Used Modules (Continued 1)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2298
Some Find and Pkg-Config Modules
■ Boost
2 https://cmake.org/cmake/help/v3.10/module/FindBoost.html
2 variables: Boost_FOUND, Boost_INCLUDE_DIRS, Boost_LIBRARY_DIRS,
Boost_LIBRARIES
2 imported targets: Boost::boost, Boost::component
■ Doxygen
2 https://cmake.org/cmake/help/v3.10/module/FindDoxygen.html
2 variables: DOXYGEN_FOUND, DOXYGEN_EXECUTABLE
2 imported targets: Doxygen::doxygen, Doxygen::dot
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2299
Some Find and Pkg-Config Modules (Continued 1)
2 variables: CMAKE_THREAD_LIBS_INIT
2 imported targets: Threads::Threads
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2300
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–2019 Michael D. Adams C++ Version: 2019-02-04 2302
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.1 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–2019 Michael D. Adams C++ Version: 2019-02-04 2303
Test Example
■ project has:
2 executable target hello
2 test target run_test
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2304
Test Example: Source Code (Including Some Scripts)
hello.cpp
1 #include <iostream>
2
3 int main() {std::cout << "Hello, World!\n";}
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2305
Test Example: CMakeLists File
CMakeLists.txt
1 # Specify minimum required version of CMake.
2 cmake_minimum_required(VERSION 3.1 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–2019 Michael D. Adams C++ Version: 2019-02-04 2306
Threads Example
■ project has:
2 executable target hello
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2307
Threads Example: Source and CMakeLists Files [With Imported Targets]
hello.cpp
1 #include <iostream>
2 #include <thread>
3
4 int main() {
5 std::thread t1([](){std::cout << "Hello, World!\n";});
6 t1.join();
7 }
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.4 FATAL_ERROR)
2 project(threads_example LANGUAGES CXX)
3
4 # Require compliance with C++11 standard.
5 set(CMAKE_CXX_STANDARD 11)
6 set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
7
8 # Find the threads library, indicating a preference for the
9 # pthread library.
10 set(THREADS_PREFER_PTHREAD_FLAG ON)
11 find_package(Threads REQUIRED)
12
13 # Define a program target called hello.
14 add_executable(hello hello.cpp)
15
16 # Set the libraries for the hello target.
17 target_link_libraries(hello Threads::Threads)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2308
Boost Log Example
■ project has:
2 executable target my_app
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2309
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–2019 Michael D. Adams C++ Version: 2019-02-04 2310
Boost Log Example: CMakeLists File [Without Imported Targets]
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.4 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–2019 Michael D. Adams C++ Version: 2019-02-04 2311
Boost Log Example: CMakeLists File [With Imported Targets]
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.5 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–2019 Michael D. Adams C++ Version: 2019-02-04 2312
OpenGL/GLFW Example
■ project has:
2 executable target trivial
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2313
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–2019 Michael D. Adams C++ Version: 2019-02-04 2314
OpenGL/GLFW Example: CMakeLists File [Without Imported Targets]
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.2 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–2019 Michael D. Adams C++ Version: 2019-02-04 2315
OpenGL/GLFW Example: CMakeLists File [With Imported Targets]
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.8 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–2019 Michael D. Adams C++ Version: 2019-02-04 2316
OpenGL/GLUT Example
■ project has:
2 executable target trivial
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2317
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–2019 Michael D. Adams C++ Version: 2019-02-04 2318
OpenGL/GLUT Example: CMakeLists File [Without Imported Targets]
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.9 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–2019 Michael D. Adams C++ Version: 2019-02-04 2319
OpenGL/GLUT Example: CMakeLists File [With Imported Targets]
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.8 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–2019 Michael D. Adams C++ Version: 2019-02-04 2320
CGAL Example
■ project has:
2 executable target orient_test
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2321
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–2019 Michael D. Adams C++ Version: 2019-02-04 2322
CGAL Example: CMakeLists File
CMakeLists.txt
1 # Specify minimum required version of CMake.
2 cmake_minimum_required(VERSION 3.1 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–2019 Michael D. Adams C++ Version: 2019-02-04 2323
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:
2 CMakeLists.txt
2 app/CMakeLists.txt
2 app/answer.cpp
2 hg2g/CMakeLists.txt
2 hg2g/answer.cpp
2 hg2g/question.cpp
2 hg2g/include/hg2g/answer.hpp
2 hg2g/include/hg2g/config.hpp.in
■ project has:
2 library target hg2g
2 executable target answer
2 option HG2G_ZAPHOD (which takes a boolean value)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2324
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–2019 Michael D. Adams C++ Version: 2019-02-04 2325
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–2019 Michael D. Adams C++ Version: 2019-02-04 2326
HG2G Example: CMakeLists Files
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.1 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–2019 Michael D. Adams C++ Version: 2019-02-04 2327
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–2019 Michael D. Adams C++ Version: 2019-02-04 2328
External Project Example
hello, hg2g, and example 100 are subdirectories containing CMake projects
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.1 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–2019 Michael D. Adams C++ Version: 2019-02-04 2329
LATEX Example
■ want to build LATEX document (i.e., produce PDF document from LATEX
source)
■ files in project:
2 CMakeLists.txt
2 main.tex
2 bib.bib
2 cmake_modules/UseLATEX.cmake
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2330
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–2019 Michael D. Adams C++ Version: 2019-02-04 2331
LATEX Example: CMakeLists File
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.1 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–2019 Michael D. Adams C++ Version: 2019-02-04 2332
Code Profiling Example: Overview
■ want to provide file that can be included in CMakeLists file that defines
option called ENABLE_PROFILING for building code with profiling enabled
■ for sake of simplicity, only consider cases of using GCC and Clang C++
compilers
■ place definition of option in file named profiling.cmake
■ include profiling.cmake in CMakeLists.txt
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2333
Code Profiling Example: profiling.cmake
profiling.cmake
1 # Define an option for enabling code profiling, which is disabled
2 # by default.
3 option(ENABLE_PROFILING "Enable code profiling with gprof." false)
4
5 if (ENABLE_PROFILING)
6 if (CMAKE_CXX_COMPILER_ID MATCHES GNU OR
7 CMAKE_CXX_COMPILER_ID MATCHES Clang)
8 # The GCC or Clang C++ compiler is being used.
9 # Add the -pg option to the flags used for compiling.
10 SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg")
11 # Add the -pg option to the linker flags used for creating
12 # executables.
13 SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg")
14 # Add the -pg option to the linker flags used for creating
15 # shared libraries.
16 SET(CMAKE_SHARED_LINKER_FLAGS
17 "${CMAKE_SHARED_LINKER_FLAGS} -pg")
18 else()
19 # Handle the case of unsupported compilers.
20 message(FATAL_ERROR
21 "Only GCC and Clang are currently supported.")
22 endif()
23 endif()
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2334
Code Profiling Example: CMakeLists.txt
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2335
Section 9.3.3
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2336
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2337
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.
3 Florent Castelli. Introduction to CMake. SwedenCpp::Stockholm 0xC,
Sundbyberg, Sweden, Apr. 26, 2018. Available online at
https://youtu.be/jt3meXdP-QI.
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2338
Part 10
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2339
Section 10.1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2340
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–2019 Michael D. Adams C++ Version: 2019-02-04 2341
Centralized Version Control
Server
Repository
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2342
Distributed Version Control
Server
Repository
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2343
Pros and Cons of Distributed Version Control
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2344
Section 10.2
Git
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2345
Git
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2346
Users of Git
■ Git has a very large user base and is employed heavily in industry
■ some organizations using Git include:
2 Apple (https://github.com/apple)
2 eBay (https://github.com/eBay)
2 Facebook (https://github.com/facebook)
2 Google (https://github.com/google)
2 Intel (https://github.com/intel)
2 Microsoft (https://github.com/Microsoft)
2 NVIDIA (https://github.com/NVIDIA)
2 Twitter (https://github.com/twitter)
2 Gnome (https://git.gnome.org)
2 Eclipse (https://git.eclipse.org)
2 KDE (https://github.com/KDE)
2 FreeDesktop (https://cgit.freedesktop.org)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2347
Repositories
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2348
Revision History and Directed Acyclic Graphs
C3 C5
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2349
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
repository
2 staging: applies changes in working tree to index
2 committing: applies changes in index to repository
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2351
Local and Remote Picture
Fetch/Pull
Push
Local Repository Remote Repository
merging changes
2 pull: propagate changes from remote repository to local repository and
merge changes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2352
HEAD
C1 C2 C3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2353
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–2019 Michael D. Adams C++ Version: 2019-02-04 2354
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–2019 Michael D. Adams C++ Version: 2019-02-04 2355
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–2019 Michael D. Adams C++ Version: 2019-02-04 2356
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–2019 Michael D. Adams C++ Version: 2019-02-04 2357
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–2019 Michael D. Adams C++ Version: 2019-02-04 2358
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–2019 Michael D. Adams C++ Version: 2019-02-04 2359
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–2019 Michael D. Adams C++ Version: 2019-02-04 2360
Git Configuration
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2361
Git on One Slide
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2362
Section 10.2.1
Basic Commands
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2363
Determining the Version of Git
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2364
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–2019 Michael D. Adams C++ Version: 2019-02-04 2365
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–2019 Michael D. Adams C++ Version: 2019-02-04 2366
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–2019 Michael D. Adams C++ Version: 2019-02-04 2367
Configuring User Information
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2368
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–2019 Michael D. Adams C++ Version: 2019-02-04 2369
Creating an Empty Repository
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2370
Cloning a Repository
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2372
Removing Files/Directories from the Index
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2373
Renaming Files
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2374
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–2019 Michael D. Adams C++ Version: 2019-02-04 2375
Committing Changes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2376
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:
2 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)
2 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–2019 Michael D. Adams C++ Version: 2019-02-04 2377
Showing Commit Logs
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2378
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–2019 Michael D. Adams C++ Version: 2019-02-04 2379
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–2019 Michael D. Adams C++ Version: 2019-02-04 2380
Showing Changes (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2381
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–2019 Michael D. Adams C++ Version: 2019-02-04 2382
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–2019 Michael D. Adams C++ Version: 2019-02-04 2383
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–2019 Michael D. Adams C++ Version: 2019-02-04 2384
.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–2019 Michael D. Adams C++ Version: 2019-02-04 2385
.gitignore File Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2386
.gitattributes Files
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2387
Tracking Empty Directories
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2388
Section 10.2.2
Remote-Related Commands
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2389
Listing, Adding, and Removing Remotes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2390
Fetching Changes from Another Repository
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2391
Pushing Changes to 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–2019 Michael D. Adams C++ Version: 2019-02-04 2393
Merging Changes
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2394
Section 10.2.3
Branch-Related Commands
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2395
Listing, Creating, and Deleting Branches
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2396
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–2019 Michael D. Adams C++ Version: 2019-02-04 2397
Section 10.2.4
Tag-Related Commands
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2398
Listing, Creating, and Deleting 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–2019 Michael D. Adams C++ Version: 2019-02-04 2400
Section 10.2.5
Miscellany
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2401
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
■ to copy repository $source_repo to local repository directory
$destination_dir, simply perform bare clone operation using
command:
git clone --bare $source_repo $destination_dir
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2402
Avoiding Repeated Passphrase Entry for SSH Authentication
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2403
Additional Remarks
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2404
Git-Related Software
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2405
Section 10.2.6
References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2406
References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2407
References II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2408
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–2019 Michael D. Adams C++ Version: 2019-02-04 2409
Part 11
Miscellaneous Tools
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2410
Section 11.1
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2411
Online C++ Compilers
2 Ideone
2 https://ideone.com
2 Coliru
2 http://coliru.stacked-crooked.com
2 Repl.It
2 https://repl.it/repls/SmallIvorySyntax
2 Compiler
. . . . . . . . . . Explorer
. . . . . . . . . (discussed in more detail shortly)
2 C++
. . . . . . . . . . . . . (discussed in more detail shortly)
Insights
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2412
Online C++ Compiler Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2413
Compiler Explorer
■ Compiler Explorer is interactive web-based compiler that supports C++, C,
and numerous other languages
■ can load, save, and edit source code
■ source code is automatically compiled as edited
■ can simultaneously display source code and corresponding assembly
code
■ can select language, compiler, and compiler options
■ source code can utilize numerous libraries (such as Boost)
■ extremely useful for understanding what assembly code is generated by
compiler
■ also helpful for quickly testing compilation of small code snippets with
variety of compilers
■ created by Matt Godbolt
■ web site running Compiler Explorer: https://godbolt.org
■ GitHub page:
https://github.com/mattgodbolt/compiler-explorer
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2414
Some Compiler Explorer Functionalities
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2416
Compiler Explorer Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2417
Example Showing GCC Code-Coverage Instrumentation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2418
Example Showing Short-Circuit Evaluation
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2419
C++ Insights
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2420
Example: Structured Bindings and Operators
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2421
Example: Lambda Expressions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2422
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2423
Section 11.2
Clang Format
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2424
Clang Format
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2425
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2426
Section 11.3
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2427
Language Server Protocol (LSP)
■ Language Server Protocol (LSP) is protocol used between editor or IDE
and server that provides language features such as completion, identifier
queries (e.g., go to definition and find all references), formatting, and
identifier renaming
■ LSP is language agnostic; servers available for many languages including
C++ and C
■ originally proposed by Microsoft
■ allows language-aware component of software to be separated from
editing component
■ server has intimate knowledge of particular programming language
■ client is typically editor or IDE
■ base protocol consists of header and content part (similar to HTTP)
■ content part of messages uses JSON-RPC to describe requests,
responses, and notifications
■ web site: https://microsoft.github.io/
language-server-protocol/specification
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2428
Clangd
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2429
Cquery
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2430
LSP Clients
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2431
Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2432
Section 11.4
YouCompleteMe (YCM)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2433
YouCompleteMe (YCM)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2434
YCM With Vim: Completion Example
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2435
Specifying Compiler Commands
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2436
Specifying Compiler Commands with CMake
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2437
YCM Commands
■ :YcmRestartServer
2 restarts ycmd completion server (e.g., if server exits for some reason)
■ :YcmForceCompileAndDiagnostics
2 forces YCM to immediately recompile file and display any new diagnostics
encountered
■ :YcmDiags
2 populates Vim’s locationlist with errors or warnings if any were detected in
file and then opens locationlist
■ :YcmShowDetailedDiagnostic
2 shows full diagnostic text when user’s cursor is on line with diagnostic
■ :YcmDebugInfo
2 displays various debug information for current file
■ :YcmToggleLogs
2 shows list of logfiles created by YCM, ycmd server, and semantic engine
server for current filetype, if any
■ :YcmCompleter
2 gives access to numerous additional IDE-like features like go to definition
and apply fix-it
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2438
Some YcmCompleter Subcommands
■ GoTo
2 performs context-sensitive goto operation based on current cursor position
(e.g., GoToInclude, GoToDeclaration, GoToDefinition, etc.)
■ GoToInclude
2 looks for header file on current line and jumps to it
■ GoToDeclaration
2 looks up identifier at current cursor position and jumps to its declaration
■ GoToDefinition
2 looks up identifier at current cursor position and jumps to its definition
■ GoToType
2 looks up identifier at current cursor position and jumps to definition of its
type
■ GetType
2 displays type of identifier at current cursor position
■ FixIt
2 attempts to make changes to buffer to correct diagnostics on current line
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2439
Vim-Related Comments
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2440
Part 12
Miscellany
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2441
Section 12.1
Miscellany
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2442
The abs Function
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2444
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–2019 Michael D. Adams C++ Version: 2019-02-04 2446
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–2019 Michael D. Adams C++ Version: 2019-02-04 2447
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–2019 Michael D. Adams C++ Version: 2019-02-04 2448
Section 12.2
Exercises
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2449
[Q.1] What Is Wrong With This Code?
main.cpp
1 #include <complex>
2 #include <iostream>
3 #include <vector>
4
5 std::complex<long double>
6 square(std::complex<long double>& x) {
7 return x * x;
8 }
9
10 void do_stuff(std::vector<int>& v) {
11 int* ip = v.data();
12 std::cout << ip << ’\n’;
13 for (std::vector<int>::iterator i = v.begin();
14 i != v.end(); ++i) {
15 std::cout << (i - v.begin()) << ’ ’ << *i << ’\n’;
16 }
17 }
18
19 int main() {
20 std::vector<int> v{1, 2, 4, 8};
21 do_stuff(v);
22 std::complex<long double> fortytwo{42};
23 std::cout << square(fortytwo) << ’\n’;
24 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2450
[Q.1] Solution: Use Const Qualifier Correctly
main.cpp
1 #include <complex>
2 #include <iostream>
3 #include <vector>
4
5 std::complex<long double>
6 square(const std::complex<long double>& x) {
7 return x * x;
8 }
9
10 void do_stuff(const std::vector<int>& v) {
11 const int* ip = v.data();
12 std::cout << ip << ’\n’;
13 for (std::vector<int>::const_iterator i = v.begin();
14 i != v.end(); ++i) {
15 std::cout << (i - v.begin()) << ’ ’ << *i << ’\n’;
16 }
17 }
18
19 int main() {
20 const std::vector<int> v{1, 2, 4, 8};
21 do_stuff(v);
22 const std::complex<long double> fortytwo{42};
23 std::cout << square(fortytwo) << ’\n’;
24 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2451
[Q.2] What Is Wrong With This Code?
main.cpp
1 #include <iostream>
2
3 class Counter {
4 public:
5 Counter(unsigned c = 0) : c_(c) {}
6 Counter(Counter& other) : c_(other.c_) {}
7 Counter(const Counter&& other) : c_(other.c_) {}
8 ˜Counter() {}
9 Counter& operator=(Counter& other)
10 {c_ = other.c_; return *this;}
11 Counter& operator=(const Counter&& other)
12 {c_ = other.c_; return *this;}
13 void clear() {c_ = 0;}
14 const unsigned get_count() {return c_;}
15 const Counter& operator++() {++c_; return *this;}
16 operator bool() {return c_ != 0;}
17 private:
18 unsigned c_; // counter value
19 };
20
21 int main() {
22 Counter zero;
23 Counter c{zero};
24 ++c;
25 std::cout << c.get_count() << ’\n’;
26 std::cout << zero.get_count() << ’\n’;
27 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2452
[Q.2] Solution: Use Const Qualifier Correctly
main.cpp
1 #include <iostream>
2
3 class Counter {
4 public:
5 Counter(unsigned c = 0) : c_(c) {}
6 Counter(const Counter& other) : c_(other.c_) {}
7 Counter(Counter&& other) : c_(other.c_) {}
8 ˜Counter() {}
9 Counter& operator=(const Counter& other)
10 {c_ = other.c_; return *this;}
11 Counter& operator=(Counter&& other)
12 {c_ = other.c_; return *this;}
13 void clear() {c_ = 0;}
14 unsigned get_count() const {return c_;}
15 Counter& operator++() {++c_; return *this;}
16 operator bool() const {return c_ != 0;}
17 private:
18 unsigned c_; // counter value
19 };
20
21 int main() {
22 const Counter zero;
23 Counter c{zero};
24 ++c;
25 std::cout << c.get_count() << ’\n’;
26 std::cout << zero.get_count() << ’\n’;
27 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2453
[Q.3] 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–2019 Michael D. Adams C++ Version: 2019-02-04 2454
[Q.3] 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–2019 Michael D. Adams C++ Version: 2019-02-04 2455
[Q.4] 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–2019 Michael D. Adams C++ Version: 2019-02-04 2456
[Q.4] 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–2019 Michael D. Adams C++ Version: 2019-02-04 2457
[Q.5] 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–2019 Michael D. Adams C++ Version: 2019-02-04 2458
[Q.5] Solution 1: 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–2019 Michael D. Adams C++ Version: 2019-02-04 2459
[Q.5] Solution 2: 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–2019 Michael D. Adams C++ Version: 2019-02-04 2460
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–2019 Michael D. Adams C++ Version: 2019-02-04 2462
[Q.6] 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–2019 Michael D. Adams C++ Version: 2019-02-04 2463
[Q.7] What Is Wrong With This Code?
foo.hpp
1 namespace foo {
2 constexpr int abs(int x);
3 }
foo.cpp
1 #include "foo.hpp"
2
3 namespace foo {
4 constexpr int abs(int x) {return (x < 0) ? (-x) : x;}
5 }
app.cpp
1 #include <iostream>
2 #include "foo.hpp"
3
4 int main() {
5 constexpr auto a = foo::abs(-42);
6 std::cout << a << ’\n’;
7 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2464
[Q.7] Solution: Define Constexpr Function in Header
foo.hpp
1 namespace foo {
2 constexpr int abs(int x) {return (x < 0) ? (-x) : x;}
3 }
app.cpp
1 #include <iostream>
2 #include "foo.hpp"
3
4 int main() {
5 constexpr auto a = foo::abs(-42);
6 std::cout << a << ’\n’;
7 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2465
[Q.8] What Is Wrong With This Code?
1 #include <cmath>
2 #include <cassert>
3
4 constexpr double func(double x) {
5 assert(x >= 0.0);
6 return std::sqrt(x) + std::sin(x) * std::cos(x);
7 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2466
[Q.8] Answer: Invalid Constexpr Function
1 #include <cmath>
2 #include <cassert>
3
4 constexpr double func(double x) {
5 assert(x >= 0.0);
6 return std::sqrt(x) + std::sin(x) * std::cos(x);
7 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2467
[Q.9] What Is Wrong With This Code?
1 #include <iostream>
2
3 constexpr unsigned int factorial(unsigned int x) {
4 unsigned int result;
5 result = 1;
6 for (unsigned int i = 2; i <= x; ++i) {
7 result *= i;
8 }
9 return result;
10 }
11
12 int main() {
13 constexpr auto x = factorial(4);
14 std::cout << x << ’\n’;
15 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2468
[Q.9] Solution: Initialize Constexpr Function Variables
1 #include <iostream>
2
3 constexpr unsigned int factorial(unsigned int x) {
4 unsigned int result = 1;
5 for (unsigned int i = 2; i <= x; ++i) {
6 result *= i;
7 }
8 return result;
9 }
10
11 int main() {
12 constexpr auto x = factorial(4);
13 std::cout << x << ’\n’;
14 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2469
[Q.10] What Is Wrong With This Code?
1 #include <vector>
2 #include <iostream>
3
4 constexpr std::vector<float> get_value(float a) {
5 return {a * 1.0f, a * 2.0f, a * 3.0f};
6 }
7
8 int main() {
9 constexpr auto v = get_value(2.0);
10 for (auto x : v) {
11 std::cout << x << ’\n’;
12 }
13 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2470
[Q.10] Solution: Constexpr Requires Literal Types
1 #include <array>
2 #include <iostream>
3
4 constexpr std::array<float, 3> get_value(float a) {
5 return {a * 1.0f, a * 2.0f, a * 3.0f};
6 }
7
8 int main() {
9 constexpr auto v = get_value(2.0);
10 for (auto x : v) {
11 std::cout << x << ’\n’;
12 }
13 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2471
[Q.11] What Is Wrong With This Code?
1 #include <algorithm>
2 #include <cstddef>
3
4 template <class T>
5 void in_place_transpose(T* a, std::size_t m, std::size_t n) {
6 T tmp[m][n];
7 std::copy_n(a, m * n, &tmp[0][0]);
8 for (std::size_t i = 0; i < m; ++i) {
9 for (std::size_t j = 0; j < n; ++j) {
10 a[j * m + i] = tmp[i][j];
11 }
12 }
13 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2472
[Q.11] Answer: Code Has At Least Two Serious Problems
1 #include <algorithm>
2 #include <cstddef>
3
4 template <class T>
5 void in_place_transpose(T* a, std::size_t m, std::size_t n) {
6 T tmp[m][n];
7 std::copy_n(a, m * n, &tmp[0][0]);
8 for (std::size_t i = 0; i < m; ++i) {
9 for (std::size_t j = 0; j < n; ++j) {
10 a[j * m + i] = tmp[i][j];
11 }
12 }
13 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2473
[Q.11] Solution: Avoid VLAs and Creating Large Stack Objects
1 #include <algorithm>
2 #include <vector>
3 #include <cstddef>
4
5 template <class T>
6 void in_place_transpose(T* a, std::size_t m, std::size_t n) {
7 std::vector<T> tmp(a, a + m * n);
8 for (std::size_t i = 0; i < m; ++i) {
9 for (std::size_t j = 0; j < n; ++j) {
10 a[j * m + i] = tmp[i * n + j];
11 }
12 }
13 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2474
[Q.C1] Cache Exercise
■ memory and cache parameters:
2 maximum memory size is 1024 bytes
2 cache size is 64 bytes
2 4-byte cache block size
2 4-way set associative cache
■ determine:
1 number of bits in address
2 log 1024 = 10 (i.e., log base 2 of number of addresses)
2
2 number of blocks in memory
2 1024/4 = 256 (i.e., memory size divided by block size)
3 number of blocks in cache
2 64/4 = 16 (i.e., cache size divided by block size)
4 number of sets in cache
2 16/4 = 4 (i.e., number of cache blocks divided by set associativity)
5 number of bits in block offset
2 log 4 = 2 (i.e., log base 2 of cache block size)
2
6 number of bits in cache index
2 log 4 = 2 (i.e., log base 2 of number of sets)
2
7 number of bits in cache tag
2 10 − (2 + 2) = 6 (i.e., number of address bits minus number of cache index and block offset bits)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2475
[Q.C1] Cache Exercise (Continued)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2476
[Q.C2] Virtual Memory Exercise
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2477
[Q.C2] Virtual Memory Exercise (Continued)
■ same virtual memory parameters as on previous slide
■ page table contains following address translation information:
VPN PPN Flags
000000002 11112 present, readable, writable, not executable
000000012 10102 present, readable, writable, not executable
100000002 00002 present, readable, not writable, executable
■ determine result of translation and protection check for accesses to
following addresses:
1 data read 1111111100000000
2
2 VPN 11111111 , PO 00000000 access violation (no address mapping)
2 2
2 instruction fetch 00000000000000002
2 VPN 00000000 , PO 00000000 ; access violation (physical address
2 2
1111000000002 )
3 data write 00000001000000002
2 VPN 00000001 , PO 00000000 ; physical address 101000000000
2 2 2
■ could virtual address xxxxxxxx000000002 map to physical address
yyyy000011112 ?
2 no (page offsets differ)
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2478
[Q.A1] What Is Wrong With This Code?
1 #include <cstddef>
2
3 // write elements of source array src to destination
4 // array dst in reverse order
5 template <class T, std::size_t N>
6 void reverse(const T (&src)[N], T (&dst)[N]) {
7 for (std::size_t i = 0; i < N; ++i) {
8 dst[N - 1 - i] = src[i];
9 }
10 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2479
[Q.A1] Answer: Aliasing Case Not Handled Correctly
1 #include <iostream>
2
3 // include definition of reverse
4 #include "aliasing_1_1.hpp"
5
6 int main() {
7 int a[4] = {1, 2, 3, 4};
8 reverse(a, a);
9 for (auto&& x : a) {
10 std::cout << x << ’\n’;
11 }
12 }
■ reverse will not work correctly if src and dst parameters refer to same
array
■ consider above usage of reverse, which is problematic
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2480
[Q.A1] Bug Fix
1 #include <cstddef>
2
3 template <class T, std::size_t N>
4 void reverse(const T (&src)[N], T (&dst)[N]) {
5 // Check for aliasing case.
6 if (src == dst) {
7 for (std::size_t i = 0; i < N / 2; ++i) {
8 using std::swap;
9 swap(dst[i], dst[N - 1 - i]);
10 }
11 } else {
12 for (std::size_t i = 0; i < N; ++i) {
13 dst[N - 1 - i] = src[i];
14 }
15 }
16 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2481
[Q.A2] What Is Wrong With This Code?
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2482
[Q.A2] Answer: Problems Caused By Aliasing
1 #include <iostream>
2
3 char* string_concat(char *, const char*);
4
5 int main() {
6 char a[1024] = "bye";
7 string_concat(a, a);
8 std::cout << a << ’\n’;
9 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2483
[Q.A3] What Is Wrong With This Code?
1 class Complex {
2 public:
3 Complex(double r, double i) : r_(r), i_(i) {}
4 double real() const {return r_;}
5 double imag() const {return i_;}
6 // What is wrong with the following function?
7 Complex& operator*=(const Complex& other) {
8 auto r = r_;
9 auto i = i_;
10 r_ = r * other.r_ - i * other.i_;
11 i_ = r * other.i_ + i * other.r_;
12 return *this;
13 }
14 // ...
15 private:
16 double r_; // real part
17 double i_; // imaginary part
18 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2484
[Q.A3] Answer: Does Not Correctly Handle Aliasing
1 #include <iostream>
2 #include "aliasing_3_1.hpp"
3
4 int main() {
5 Complex a(1.0, 1.0);
6 Complex b(a);
7 b *= a;
8 a *= a;
9 // The value of b is correct.
10 std::cout << b.real() << ’ ’ << b.imag() << ’\n’;
11 // The value of a is not correct.
12 std::cout << a.real() << ’ ’ << a.imag() << ’\n’;
13 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2485
[Q.A3] Bug Fix
1 class Complex {
2 public:
3 Complex(double r, double i) : r_(r), i_(i) {}
4 double real() const {return r_;}
5 double imag() const {return i_;}
6 Complex& operator*=(const Complex& other) {
7 auto r = r_;
8 auto i = i_;
9 auto other_r = other.r_;
10 auto other_i = other.i_;
11 r_ = r * other_r - i * other_i;
12 i_ = r * other_i + i * other_r;
13 return *this;
14 }
15 // ...
16 private:
17 double r_; // real part
18 double i_; // imaginary part
19 };
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2486
[Q.ST1] Triangle Program
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2487
[Q.ST1] Triangle Program: Test Strategy
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2488
[Q.LR1] 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–2019 Michael D. Adams C++ Version: 2019-02-04 2489
[Q.I1] What Is the Behavior of This Code?
1 class Widget {
2 public:
3 Widget() {}
4 int i() const {return i_;}
5 private:
6 int i_;
7 };
8
9 int main() {
10 Widget w;
11 return w.i();
12 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2490
[Q.I2] What Is the Behavior of This Code?
1 class Widget {
2 public:
3 Widget() : i_() {}
4 int i() const {return i_;}
5 private:
6 int i_;
7 };
8
9 int main() {
10 Widget w;
11 return w.i();
12 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2491
[Q.I3] What Is the Behavior of This Code?
1 class Widget {
2 public:
3 Widget() {}
4 int i() const {return i_;}
5 private:
6 int i_{};
7 };
8
9 int main() {
10 Widget w;
11 return w.i();
12 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2492
[Q.I4] What Is the Behavior of This Code?
1 #include <iostream>
2
3 // aggregate
4 struct Widget {
5 int i = 0;
6 int j = 0;
7 };
8
9 std::ostream& operator<<(std::ostream& out, const Widget& w) {
10 return out << w.i << ’,’ << w.j;
11 }
12
13 int main() {
14 Widget a;
15 Widget b{};
16 Widget c{1};
17 Widget d{1, 2};
18 std::cout << a << ’ ’ << b << ’ ’ << c << ’ ’ << d << ’\n’;
19 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2493
Moving-Versus-Copying Example
1 #include <iostream>
2 #include <utility>
3
4 class Widget {
5 public:
6 Widget() {std::cout << "default ctor\n";}
7 ˜Widget() {std::cout << "dtor\n";}
8 Widget(const Widget&) {std::cout << "copy ctor\n";}
9 Widget(Widget&&) {std::cout << "move ctor\n";}
10 Widget& operator=(const Widget&)
11 {std::cout << "copy assign\n"; return *this;}
12 Widget& operator=(Widget&&)
13 {std::cout << "move assign\n"; return *this;}
14 // ...
15 };
16
17 Widget get_value() {return Widget();}
18
19 // Note: returning a const object is a bad idea
20 const Widget get_const_value() {return Widget();}
21
22 int main() {
23 Widget s(get_value()); // guaranteed elided move
24 Widget t(s); // copy construction
25 Widget u(std::move(t)); // move construction
26 Widget v(get_const_value()); // guaranteed elided copy
27 s = t; // copy assignment
28 s = get_value(); // move assignment
29 s = std::move(t); // move assignment
30 s = get_const_value(); // copy assignment (not move!)
31 }
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2494
std::string Concatenation
■ Unnecessary temporaries!
■ Fix:
func(s + ", " + p);
func(p + ", "s + s);
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2495
std::vector<std::string> Insertion
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2496
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–2019 Michael D. Adams C++ Version: 2019-02-04 2497
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–2019 Michael D. Adams C++ Version: 2019-02-04 2498
Types of Literals
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2499
Part 13
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2500
Limits of Knowledge
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2501
C++ References
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2502
C++ References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2503
C++ References II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2504
C++ References III
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2505
C++ References IV
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2506
C++ References V
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2507
C++ References VI
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2508
Other C++ References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2509
Other C++ References II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2510
Other C++ References III
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2511
Yet More C++ References I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2512
Yet More C++ References II
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2513
Miscellaneous Talks I
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2514
C++ Programming Competitions
Copyright ⃝
c 2015–2019 Michael D. Adams C++ Version: 2019-02-04 2515
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–2019 Michael D. Adams C++ Version: 2019-02-04 2516