Você está na página 1de 302

Topics From Effective C++ Programming

Topics from Effective C++ Programming

bit.ly/NVIDIA_Cpp_Topics

Kalb for NVIDIA


http://cpp.training/

Kalb for NVIDIA


http://cpp.training/

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Topics from Effective C++ Programming

Scott Meyers, Ph.D.


Software Development Consultant
smeyers@aristeia.com
http://www.aristeia.com/

Scott Meyers, Software Development Consultant Kalb for NVIDIA


http://www.aristeia.com/ http://cpp.training/

Topics from Effective C++ Programming

Scott Meyers, Ph.D.


Software Development Consultant
smeyers@aristeia.com
http://www.aristeia.com/

Scott Meyers, Software Development Consultant Kalb for NVIDIA


http://www.aristeia.com/ http://cpp.training/

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Jon Kalb
• 25 years of professional C++ development
• Chair of
•CppCon
•C++Now
•Silicon Valley Code Camp C++ Track
•Boost Steering Committee
• Taught C++ at Golden Gate University Grad School
• Co-Author of “C++ Today: The Beast is Back”
• Microsoft MVP
• jon@cpp.training
• @_JonKalb
• linkedin.com/in/jonkalb

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 6

Agenda
§ Type deduction
§ Initialization
§ const
§ Range based for loops
§ Introduction to Rvalue References and Move Semantics
§ Type fundamentals / Special member functions
§ Move Semantics and Perfect Forwarding Best Practices
§ STL / Algorithms / Containers
§ Generalized Function Objects
§ Lambda expressions
§ RAII and Smart Pointers
§ Efficiency
§ That’s a lot of topics.
Æ There may not be time to cover everything.
w We’ll definitely cover the most important material.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 7

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Always on the Agenda


§ Your questions, comments, topics, problems, etc.
Æ Always top priority.
The primary course goal is to cover what you want to know.
§ It doesn’t matter whether it’s in the prepared materials.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 8

Course Approach
Based on guidelines:
§ Guidelines are a lot easier to remember than the ISO standard.
§ You have to understand the rationale behind the guidelines.
§ Guidelines emphasize what you should do, not what you shouldn’t do.
Code examples are only fragments:
§ Most parts of a program are dull.
§ Fragments isolate important concepts, focus your attention.
§ Complete programs span multiple slides.
Æ Awkward for both instructor and student.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 9

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

The Federated Languages of C++


C++ can be easier to comprehend if you think of it as a federation of several
different (but clearly related) languages:
§ C89
§ Object-Oriented C++
§ Template C++
Æ Template programming in general
Æ TheStandard Template Library (STL)
Æ Template Metaprogramming (TMP)

In this course, we’ll focus on the highlighted “sub-languages” of C++.

Based on EC++/3E Item 1.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 10

History and Vocabulary


1998: ISO C++ Standard officially adopted (“C++98”).
§ 776 pages.
2003: TC1 (“Technical Corrigendum 1”) published (“C++03”).
§ Bug fixes for C++98.
2005: TR1 (Library “Technical Report 1”) published.
§ 14 likely new components for the standard library.
2011: “C++0x” ratified ⇒ “C++11”.
§ 1353 pages.
2014: C++14 ratified.
§ 1372 pages.
2017: C++17 ratified. Current C++ Standard.

In this course, “C++11” generally means “C++11 and C++14.”


§ C++11 is subset of C++14.
“Modern” means C++11 or later and “classic” means pre-C++11.
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 11

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Why?
Why bother?
§ The specification for Modern C++ is twice as long as for Classic C++.
o Classic C++ is complicated enough.
§ Modern C++ is a moving target, there will always be something new to
learn.
§ Classic C++ is Turing complete.
o Any program that can be written can be written in C++98.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 12

Why?
Why do we program in C++?
§ Power type deduction
o Low-level control uniform initialization
o High-level abstractions const
o Clarity and expressivity range-based for loops
o No compromise on performance moving & forwarding
§ More! More! More! type fundamentals
o Modern C++ gives us more of all of these STL, algorithms, cont.
o Convenience: fewer places for bugs to hide lambdas
o Also better safety generalized func. obj.
o Type safety smart pointers.
efficiency
o Resource management

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 13

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

A Code Example
int const y(5); int const y{5};
int const x = 5; int const x{5};
int arr[] = { 5, 10, 15 }; int arr[]{ 5, 10, 15 };
struct Point1 { int x, y; }; struct Point1 { int x, y; };
Point1 const p1 = { 10, 20 }; Point1 const p1{ 10, 20 };
struct Point2 { struct Point2 {
Point2(int x, int y); Point2(int x, int y);
… …
}; };
Point2 const p2(10, 20); Point2 const p2{10, 20};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 14

Another Code Example


struct FtoC {
void operator()(std::pair<std::string const, double>& p) const {
p.second = (p.second - 32) * 5 / 9;
}
};
int main() {
std::pair<std::string, double> states[] = {
std::make_pair("California", 59.4),
std::make_pair("Alaska", 26.6),
std::make_pair("Hawaii", 70.0)};
std::map<std::string, double>
mean_temp(states,
states + sizeof(states) / sizeof(std::pair<std::string, double>));

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 15

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Another Code Example (cont.)


for (std::map<std::string, double>::const_iterator itr(
mean_temp.begin()); itr != mean_temp.end(); ++itr) {
std::cout << itr->first << " (" << itr->second << " F)\n";
}
std::for_each(mean_temp.begin(), mean_temp.end(), FtoC());
for (std::map<std::string, double>::const_iterator
itr(mean_temp.begin()); itr != mean_temp.end(); ++itr) {
std::cout << itr->first << " (" << itr->second << " C)\n";
}
}
Alaska (26.6 F)
California (59.4 F)
Hawaii (70 F)
Alaska (-3 C)
California (15.2222 C)
Hawaii (21.1111 C)

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 16

initializer list
eliminates
initializing array
Another Code Example
int main() {
std::map<std::string, double> mean_temp{ {"California", 59.4},
{"Alaska", 26.6},
{"Hawaii", 70.0}};
for (auto t: mean_temp) { for each loop
std::cout << t.first << " (" << t.second << " F)\n";
}
auto type deduction
std::for_each(begin(mean_temp), end(mean_temp),
[] (auto& p) {p.second = (p.second - 32) * 5 / 9;});
lambda eliminates
for (auto t: mean_temp) { function object
std::cout << t.first << " (" << t.second << " C)\n";
}
}

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 17

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Type Deduction

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 18

Type Deduction Review (Classic C++)


Only happens for function templates, never for class templates.
Template types are deduced by the compiler based on the parameters
passed at the function call site.
template<class T> void f(T t);

f(expr); // deduce t’s type from expr

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 19

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Type Deduction Review (Classic C++)


template<class T> void f(T t);

f(expr); // deduce t’s type from expr

int i0(42);
int const i1(i0);
int& i2(i0);
int const& i3(i0);
f(i0); // deduce t’s type as int
f(i1); // deduce t’s type as int
f(i2); // deduce t’s type as int
f(i3); // deduce t’s type as int
For variables not explicitly declared to be a reference:
§ Top-level consts/volatiles in the initializing type are ignored.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 20

Type Deduction Review (Classic C++)


template<class T> void f(T& t);

f(expr); // deduce t’s type from expr

int i0(42);
int const i1(i0);
int& i2(i0);
int const& i3(i0);
f(i0); // deduce t’s type as int&
f(i1); // deduce t’s type as int const&
f(i2); // deduce t’s type as int&
f(i3); // deduce t’s type as int const&
For variables explicitly declared to be a reference:
§ Top-level consts/volatiles in the initializing type cannot be ignored.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 21

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

auto for Type Declarations


Type deduction for auto is akin to that for template parameters:
template<class T> void f(T t);

f(expr); // deduce t’s type from expr
auto v = expr; // do essentially the same thing for v’s type
auto v(expr); // preferred syntax for initialization (no “=”)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 22

auto for Type Declarations


auto variables have the type of their initializing expression:
auto x1(10); // x1: int
std::map<int, std::string> m;
auto i1(begin(m)); // i1: std::map<int, std::string>::iterator
const/volatile and reference/pointer adornments may be added:
auto const* x2(&x1); // x2: int const*
auto const& i2(m); // i2: std::map<int, std::string> const&
Will this gives us a const_iterator?:
auto const i1(begin(m)); // i1: std::map<int, std::string>::iterator const
To get a const_iterator, use the new cbegin container function:
auto ci(m.cbegin()); // ci: std::map<int, std::string>::const_iterator
auto ci(cbegin(m)); // C++14 introduces free standing cbegin()
§ cend, crbegin, and crend exist, too.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 23

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

auto for Type Declarations


For variables not explicitly declared to be a reference:
§ Top-level consts/volatiles in the initializing type are ignored.
§ Array and function names in initializing types decay to pointers.
std::list<int> const li;
auto v1(li); // v1: std::list<int>
auto& v2(li); // v2: std::list<int> const&
float data[BufSize];
auto v3(data); // v3: float*
auto& v4(data); // v4: float (&)[BufSize]

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 24

auto for Type Declarations


Both direct and copy initialization syntaxes are permitted.
auto v1(expr); // direct initialization syntax
auto v2 = expr; // copy initialization syntax
For auto, both syntaxes have the same meaning.
• If the type of expr has an explicit copy constructor, only direct
initialization is permitted

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 25

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

What is “explicit”
Imagine a rational number type:
struct Rational
{
Rational(int num = 0, int den = 1); // Initial value

};
Rational operator+(Rational const&, Rational const&);

Rational r0, r1;

r0 = r1 + 6; // Do we want this to compile?
As written, this will compile.

The compiler looks for a way to convert an int (6) to a Rational.


The constructor can be used to do that, so the code compiles.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 26

What is “explicit”
Imagine a container type:
template<class T> struct Vector
{
Vector(int num = 0); // Initial size

};

int Mean(Vector<int> const&); // Calcs average

Mean(7); // Do we want this to compile?

As written, this will compile.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 27

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

What is “explicit”
Imagine a container type:
template<class T> struct Vector
{
explicit Vector(int num = 0); // Initial size

}; Declaring the constructor explicit, means that …

int Mean(Vector<int> const&); // Calcs average

Mean(7);
… this will not compile.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 28

What is “explicit”
Imagine a container type:
template<class T> struct Vector
{
explicit Vector(int num = 0); // Initial size

}; Declaring the constructor explicit, means that …

int Mean(Vector<int> const&); // Calcs average

Mean(Vector<int>(7) ); … the constructor can be used only if
the type name is explicitly stated.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 29

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

What is “explicit”
Any (non-copy) constructor that can be called with only one parameter is a
conversion constructor. This is true whether the constructor
• has only one parameter, or
• there are default values for all but (at most) one parameter.

The implicit conversion problem is only an issue for conversion


constructors.
• Guideline: Declare conversion constructors as explicit if you do not
want to support implicit conversion from the parameter type.
• Experience has shown that implicit conversions are problematic, so in
most cases you’ll want to disallow these implicit conversions.

As is often the case with C++, we got the default wrong.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 30

Initialization
Uniform Initialization
Initializer Lists

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 31

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Uniform Initialization Syntax


C++98 offers multiple initialization forms.
§ Initialization ≠ assignment.
Æ E.g., const objects can be initialized, not assigned.
Examples:
int const y(5); // “direct initialization” syntax
int const x = 5; // “copy initialization” syntax
int arr[] = { 5, 10, 15 }; // brace initialization
struct Point1 { int x, y; };
Point1 const p1 = { 10, 20 }; // brace initialization
struct Point2 {
Point2(int x, int y);

};
Point2 const p2(10, 20); // function call syntax

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 32

Initialization in C++98
Containers require another container:
int vals[] = { 10, 20, 30 };
std::vector<int> const cv(vals, vals+3); // init from another
// container
Member and heap arrays are impossible:
struct Widget {
Widget(): data(???) {}
private:
int const data[5]; // not initializable
};
float const* pData = new float const[4]; // not initializable

Note: In Modern C++, we don’t recommend either C-style arrays or new:

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 33

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Uniform Initialization Syntax


Brace initialization syntax now (as of C++11) allowed everywhere:
int const val1 {5};
int const val2 {5};
int a[] { 1, 2, val1, val1+val2 };
struct Point1 { … }; // as before
Point1 const p1 {10, 20};
struct Point2 { … }; // as before
Point2 const p2 {10, 20}; // calls Point2 ctor
std::vector<int> const cv { a[0], 20, val2 };
struct Widget {
Widget(): data {1, 2, a[3], 4, 5} {}
private:
int const data[5];
};
float const * pData = new float const[4] { 1.5, val1-val2, 3.5, 4.5 };

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 34

Uniform Initialization Syntax


Really, everywhere:
Point2 makePoint() { return { 0, 0 }; } // return expression;
// calls Point2 ctor
void f(std::vector<int> const& v); // func. declaration

f({ val1, val2, 10, 20, 30 }); // function argument

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 35

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Uniform Initialization Syntax


Semantics differ for aggregates and non-aggregates:
§ Aggregates (e.g., arrays and structs):
Æ Initialize members/elements beginning-to-end.
§ Non-aggregates:
Æ Invoke a constructor.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 36

Brace-Initializing Aggregates
Initialize members/elements beginning-to-end.
§ Too many initializers ⇒ error.
§ Too few initializers ⇒ remaining objects are value-initialized:
Æ Built-in
types initialized to 0.
Æ UDTs with constructors are default-constructed.
Æ UDTs without constructors: members are value-initialized.
struct Point1 { int x, y; }; // as before
Point1 const p1 = { 10 }; // same as { 10, 0 }
Point1 const p2 = { 1, 2, 3 }; // error! too many
// initializers
Æ std::array is also an aggregate:
long f();
std::array<long, 3> arr = { 1, 2, f(), 4, 5 }; // error! too many
// initializers

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 37

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Brace-Initializing Non-Aggregates
Invoke a constructor.
struct Point2 { // as before
Point2(int x, int y);

};
short a, b;

Point2 const p1 {a, b}; // same as p1(a, b)
Point2 const p2 {10}; // error! too few ctor args
Point2 const p3 {5, 10, 20}; // error! too many ctor args
§ True even for containers (details shortly):
std::vector<int> v { 1, a, 2, b, 3 }; // calls a vector ctor
std::unordered_set<float> s { 0, 1.5, 3 }; // calls an
// unordered_set ctor

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 38

Uniform Initialization Syntax


Brace-initialized variables may use “=“:
int const val1 = {5};
int const val2 = {5};
int a[] = { 1, 2, val1, val1+val2 };
struct Point1 { … };
Point1 const p1 = {10, 20};
struct Point2 { … };
Point2 const p2 = {10, 20};
std::vector<int> const cv = { a[0], 20, val2 };

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 39

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Uniform Initialization Syntax


Other uses of brace initialization can’t use “=“:
struct Widget {
Widget(): data = {1, 2, a[3], 4, 5} {} // error!
private:
int const data[5];
};
float const * pData =
new float const[4] = { 1.5, val1-val2, 3.5, 4.5 }; // error!
Point2 makePoint() { return = { 0, 0 }; } // error!

void f(std::vector<int> const & v); // as before

f( = { val1, val2, 10, 20, 30 }); // error!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 40

Uniform Initialization Syntax


And “T var = expr“ syntax can’t call explicit constructors:
struct Widget {
explicit Widget(int);

};
Widget w1(10); // okay, direct init: explicit ctor callable
Widget w2{10}; // ditto
Widget w3 = 10; // error! copy init: explicit ctor not callable
Widget w4 = {10}; // ditto

Develop the habit of using brace initialization without “=“.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 41

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Uniform Initialization Syntax


Uniform initialization syntax a feature addition, not a replacement.
§ Almost all initialization code valid in C++98 remains valid.
Æ Rarely a need to modify existing code.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 42

Brace Initialization and Implicit Narrowing


Sole exception: implicit narrowing.
§ C++98 allows it via brace initialization, C++11 doesn’t:
struct Point { int x, y; };
Point p1 = { 1, 2.5 }; // fine in C++98:
// implicit double ⇒ int
// conversion;
// error in C++11
Point p2 = { 1, static_cast<int>(2.5) }; // fine in both C++98
// and C++11

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 43

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Brace Initialization and Implicit Narrowing


Direct constructor calls and brace initialization thus differ subtly:
struct Widget {
Widget(unsigned u);

};
int i;

Widget w1(i); // okay, implicit int ⇒ unsigned
Widget w2 {i}; // error! int ⇒ unsigned narrows
unsigned u;
Widget w3(u); // fine
Widget w4 {u}; // also fine, same as w3’s init.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 44

Initializer Lists
A mechanism to generalize array aggregate initialization:
§ Available to all UDTs.
int x, y;

int a[] { x, y, 7, 22, -13, 44 }; // array initialization
std::vector<int> v { 99, -8, x-y, x*x }; // std. library type
Widget w { a[0]+a[1], x, 25, 16 }; // arbitrary UDT
§ Available for more than just initialization, e.g.
std::vector<int> v {}; // initialization
v.insert(v.end(), { 99, 88, -1, 15 }); // multi-element insertion
v = { 0, 1, x, y }; // replace v’s value
Æ Any function can use an “initializer” list.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 45

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Initializer Lists
Approach startlingly simple:
§ Brace initializer lists convertible to std::initializer_list objects.
§ Functions can declare parameters of this type.
§ std::initializer_list stores initializer values in an array and offers these
member functions:
Æ size // # of elements in the array
Æ begin // ptr to first array element
Æ end // ptr to one-beyond-last array element

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 46

Initializer Lists
#include <initializer_list> // necessary header
std::u16string getName(int ID); // lookup name with given ID

struct Widget {
Widget(std::initializer_list<int> nameIDs)
{
names.reserve(nameIDs.size());
for (auto id: nameIDs) names.push_back(getName(id));
}
private:
std::vector<std::u16string> names;
};
...
Widget w { a[0]+a[1], x, 25, 16 }; // copies values into an array
// wrapped by an initializer_list
// passed to the Widget ctor.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 47

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Initializer Lists
std::initializer_list parameters may be used with other parameters:
struct Widget {
Widget( std::string const& name, double epsilon,
std::initializer_list<int> il);

};
std::string name("Buffy");
Widget w {name, 0.5, // same as
{5, 10, 15} }; // Widget w(name, 0.5,
// std::initializer_list({5,10,15}));
§ Note the nested brace sets.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 48

Initializer Lists
They may be templatized:
struct Widget {
template<class T> Widget(std::initializer_list<T> il);
...
};
...
Widget w1 { -55, 25, 16 }; // fine, T = int
Only homogeneous initializer lists allow type deduction to succeed:
Widget w2 { -55, 2.5, 16 }; // error, T can’t be deduced

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 49

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Initializer Lists and Overload Resolution


When resolving constructor calls, std::initializer_list parameters are
preferred for brace-delimited arguments:
struct Widget {
Widget(double value, double uncertainty); // #1
Widget(std::initializer_list<double> values); // #2

};
double d1, d2;

Widget w1 { d1, d2 }; // calls #2
Applies only to brace-delimited arguments:
Widget w2(d1, d2); // calls #1

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 50

Initializer Lists and Overload Resolution


Not of only theoretical interest:
template < class T, // from the C++11
class Allocator = allocator<T> > // standard
struct vector {
...
vector(size_type n, T const& value, Allocator const & = Allocator());
vector(initializer_list<T>, Allocator const & = Allocator());
...
};

std::vector<int> v1(10, 5); // v1.size() == 10, all values == 5


std::vector<int> v2{10, 5}; // v2.size() == 2, values == {10, 5}
Choose carefully between {} and () when initializing objects!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 51

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Braced Initializers and auto


auto deduces std::initializer_list for braced initializers:
auto i{ 2, 4, 6, 8 }; // i is std::initializer_list<int>
In general, templates deduce no type for braced initializers:
template<class T>
void f(T param) { ... }
f({ 2, 4, 6, 8}); // error! no type deduced for
// “{ 2, 4, 6, 8 }”
§ Only way that auto type deduction ≢ template type deduction.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 52

Braced Initializers and auto


Especially for single-element braced initializers, this can confuse:
auto i1 = 10; // i1 is int
auto i2(10); // i2 is int
auto i3{10}; // i3 is std::initializer_list<int>
Particularly when such variables interact with overload resolution:
std::vector<int> v1(i1); // v1.size() == 10, values == 0
std::vector<int> v2(i2); // v2.size() == 10, values == 0
std::vector<int> v3(i3); // v3.size() == 1, value == 10
Use care when initializing auto variables with braced initializers!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 53

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Braced Initializers and auto


A change for C++17 allows us to use uniform initializer syntax:

auto v1{expr};
We’ll cover decltype in a
auto x1{3}; // decltype(x1) is int later section.
auto x2{1, 2}; // error: not a single element
auto x3 = {3}; // decltype(x3) is std::initializer_list<int>
auto x4 = {1, 2}; // decltype(x4) is std::initializer_list<int>
auto x5 = {1, 2.0}; // error: cannot deduce element type

“the feeling amongst the vendors when this was discussed in


CWG was that this was a defect against C++14 and not a new
feature” — Jonathan Caves

For all versions of the language as of For these slides, we don’t


VS 2015, GCC 5.1 & Clang 3.8. assume this change.
Kalb for NVIDIA Copyrighted material, all rights reserved.
http://cpp.training/ Slide 54

Initializer Lists and Overload Resolution


Given multiple std::initialization_list candidates, best match is determined
by worst element conversion:
struct Widget {
Widget(std::initializer_list<int>); // #1
Widget(std::initializer_list<double>); // #2
Widget(std::initializer_list<std::string>); // #3
...
};
Widget w1 { 1, 2.0, 3 }; // int ⇒ double same rank as
// double ⇒ int, so ambiguous
Widget w2 { 1.0f, 2.0, 3.0 }; // float ⇒ double better than
// float ⇒ int, so calls #2
std::string s;
Widget w3 { s, "Init", "Lists" }; // calls #3

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 55

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Initializer Lists and Overload Resolution


If best match involves a narrowing conversion, call is invalid:
struct Widget {
Widget(std::initializer_list<int>);
Widget(int, int, int);
};
Widget w { 1, 2.0, 3 }; // error! double ⇒ int narrows

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 56

Uniform Initialization Summary


§ Brace initialization syntax now available everywhere.
Æ Aggregates initialized top-to-bottom/front-to-back.
Æ Non-aggregates initialized via constructor.
§ Implicit narrowing not allowed.
§ std::initializer_list parameters allow “initialization” lists to be passed to
functions.
Æ Not actually limited to initialization (e.g., std::vector::insert).

§ Choose carefully between {} and () when initializing objects.


Æ Remember that auto + { expr } yields std::initializer_list.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 57

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

const

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 58

Use const Whenever Possible


Overview:
§ The beauty of const
§ const and pointers
Æ The strange case of string literals
Æ The stranger case of type conversions
§ const and pass-by-value
§ const and return-by-value
§ const member functions:
Æ Conceptual constness vs. bitwise constness
Æ The effect of mutable
§ casting away constness
§ The lifetime of const
§ const and type aliases (using or typedef)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 59

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

The Beauty of const


Using const allows you to specify a semantic constraint:
§ Something should not be modified
§ You state this explicitly:
Æ Other programmers can see it
Æ Compilers can see it
§ Compilers will enforce it
It can be used:
§ Outside structs: for global, namespace, and static objects
§ Inside structs: for static and nonstatic members
§ In functions:
Æ Parameter declarations
Æ Return type declarations
Æ Local static and automatic variables
Æ For nonstatic member functions, the function as a whole

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 60

const and Pointers


Both pointer and what’s pointed to can be const:

char greeting[ ]{"Hello"};

char *p{greeting}; // non-const pointer,


// non-const data
char * const p{greeting}; // const pointer,
// non-const data
char const *p{greeting}; // non-const pointer,
// const data
char const * const p{greeting}; // const pointer,
// const data

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 61

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

const Syntax
The rule for how const is applied is:
• The const always applies to what is to its left.
• Unless nothing is to its left, then it applies to what is on the right
char greeting[ ]{"Hello"};

char *p{greeting}; // non-const pointer,


// non-const data
char * const p{greeting}; // const pointer,
// non-const data
char const *p{greeting}; // non-const pointer,
// const data
char const * const p{greeting}; // const pointer,
// const data

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 62

const Syntax
The rule for how const is applied is:
• The const always applies to what is to its left.
• Unless nothing is to its left, then it applies to what is on the right
char greeting[ ]{"Hello"};

char *p{greeting}; // non-const pointer,


// non-const data
char * const p{greeting}; // const pointer,
// non-const data

Also legal, more common, less clear:


const char *p{greeting}; // non-const pointer,
// const data
Putting const
const char * const p{greeting}; // const pointer, always on the
// const data
right is called
“East const.”
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 63

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

const and String Literals


String literals (e.g., "Hello") have type char const [ ]:
§ char const[ ] typically “decays” to char const*
§ Function overloading shows that string literals are not of type char*
void someFunction(char *someString); // A
void someFunction(char const*someString); // B
someFunction("Wombat"); // calls B, not A

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 65

const and String Literals


The following violates const correctness:
Widget const *pcw{new Widget};
Widget *pw{pcw}; // error! can’t initialize a
// Widget* with a Widget const*
We’d thus expect this to be an error:
char *animal{"Wombat"}; // initialize a char*
// with a const char*
And it is an error.
But it didn’t used to be.
The Classic C++ type system granted this legacy usage a dispensation.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 66

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

const and String Literals


Hence:
§ String literals have type char const [ ] (essentially char const*)
Æ Overloading takes that into account
§ In Classic C++, you could initialize char* pointers with string literals
Æ Itwas generally a bad idea
Æ Itwas officially deprecated
Æ As in C, trying to modify a string literal via a char* pointer yielded
undefined results
§ In Modern C++, you cannot initialize char* pointers with string literals
Æ It is not allowed
Æ In practice, the compiler may issue a warning instead of an error

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 67

const and Pointer Type Conversions


Converting T* to T const* is fine:
Widget *pw{new Widget};
Widget const*pcw{pw}; // fine, converts Widget* to
// Widget const*
Converting T** to T const** is not fine:
Widget const**ppcw{&pw}; // error! can’t convert Widget** to
// Widget const**

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 68

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

const and Pointer Type Conversions


Consider what could happen if it were allowed:
Widget const cw{value1}; // cw initially has value1
Widget *pw;
Widget const**ppcw{&pw}; // this won’t compile,
// but suppose it did
ppcw pw cw:value1

*ppcw = &cw; // *ppcw and pw now


// point to cw
ppcw pw cw:value1

*pw = value2; // cw is modified!


ppcw pw cw:value2

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 69

const and Pointer Type Conversions


The allowed (and safe) conversion is from T** to T const* const*:
Widget *pw{new Widget};
Widget const*pcw{pw}; // fine, converts Widget* to
// Widget const*
Widget const * const *ppcw{&pw}; // also fine, converts
// Widget** to
// Widget const* const*
Note how this prevents the problem of the previous page:
Widget const cw;
Widget *pw;
Widget const* const *ppcw{&pw}; // fine, safe
*ppcw = &cw; // error! *ppcw is a
// const pointer

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 70

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

const and Pass-By-Value


From a caller’s point of view, const and pass-by-value is redundant:
double cubeRoot(double const d);
double const pi{3.14159};
std::cout << cubeRoot(pi); // pi can’t be changed,
// even w/o const
Compilers know this, so they ignore const if it’s applied to by-value
parameters in function declarations:
double cubeRoot(double d); // this declares the
// same function as
// the one above
Declaring by-value parameters const suggests to readers that the author of
the declaration doesn’t really understand how C++ works.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 72

const and Pass-By-Value


Conclusion: never combine const and pass-by-value.
§ This applies only to interfaces
§ Implementers may choose to combine const and pass-by-value to
prevent implementation errors
Æ This can be changed later without affecting clients
double cubeRoot(double d); // in .h file
double cubeRoot(double const d) // in .cpp file
{
... // compiler will reject
} // modifications to d
Æ Similarly, it can be convenient to declare local objects const:
void doSomething(std::vector<int>& data)
{
int const lastValue{data.back()};
...
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 73

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Rvalue semantics
Will this compile?:
int a{1};
int b{2};
int c{3};
a + b = c;
The compiler will not allow us to modify temporaries (rvalues) of
fundament types.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 74

Rvalue semantics
Return-by-value is often useful:
§ It allows for complicated expressions.
Arithmetic operators are a good example:
struct UPInt { ... }; // "unlimited precision integers"
UPInt operator+(UPInt const& lhs, UPInt const& rhs);
UPInt operator/(UPInt const& lhs, UPInt const & rhs);
UPInt a, b, c;
...
c = (a + b) / b; // same as
// c.operator=(operator/(operator+(a,b), b))

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 75

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Rvalue semantics
Will this compile?:
struct UPInt { ... }; // as before
UPInt operator+(UPInt const& lhs, UPInt const& rhs); // as before
UPInt operator/(UPInt const& lhs, UPInt const& rhs); // as before
UPInt a, b, c;
...
a + b = c;
Yes. It is the same as operator+(a, b).operator=(c).
Do we want it to compile?

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 76

Return-By-const-Value
Return-by-const-value can be useful:
§ This is a way to make objects act like rvalues for fundament types

struct UPInt { ... }; // "unlimited precision integers"


UPInt const operator+(UPInt const& lhs, UPInt const& rhs);
UPInt a, b, c;
...
a + b = c; // same as operator+(a,b).operator=(c)
Because operator+ returns a const object, the last line won’t compile!
Neither will this:
if (a + b = c) ... // oops, used "=" instead of "=="
§ This could compile if an implicit UPInt ⇒ bool conversion exists.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 77

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Return-By-const-Value
Any changes made to the value of a temporary, will be lost at the end of the
expression, when the temporary is destroyed.
Modifying a temporary is suspect because the changes are going to be lost
and the time/work to make them are wasted.
Is there any time that we’d want to modify a temporary?
What data is held by the UPInt class?
It may be that we’d like to take the allocated array of bits from a temporary
UPInt rather than allocating a new array and copying the bits. This is
possible in Modern C++ and we call it Move Semantics.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 78

Guideline
§ Return const objects when you want to emulate rvalue semantics, but
not if you want to enable move operations.
§ If the type has resources that can be moved, we want to enable move
operations.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 79

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

const Member Functions


const member functions promise not to modify the object on which they
are invoked:
§ Only const member functions can be invoked on const objects:
Æ Declare as many functions const as you can
§ Non-const objects can call both const and non-const member functions.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 80

const Member Functions


Does C++ support self-modifying code?
Then aren’t all C++ functions “const”?
What is const in a const member function?
struct MyType {
MemberFunction(int a_parameter);
ConstMemberFunction(int a_parameter) const;
};
What are the real parameters to these function?
struct MyType {
MemberFunction(MyType* this, int a_parameter);
ConstMemberFunction(MyType const* this, int a_parameter) const;
};
In a const member function, the implied “this” pointer is const.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 81

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

const Member Functions


Examples:
struct String {
void uppercasify();
std::size_t length() const;

};
void convertAndPrint(String const& s)
{
s.uppercasify(); // error!
std::cout << s << " (Length = "
<< s.length() // fine
<< ")\n";
}
void convertAndPrint(String& s)
{
s.uppercasify(); // fine
std::cout << s << " (Length = "
<< s.length() // fine
<< ")\n";
}
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 82

Overloading on const
Member functions differing only in their constness can be overloaded:
struct String {
...
char& operator[ ](int position)
{ return data[position]; }
char const& operator[ ](int position) const
{ return data[position]; }
private:
char *data;
};
String s1{"Hello"};
std::cout << s1[0]; // calls non-const String::operator[ ]
String const s2{"World"};
std::cout << s2[0]; // calls const String::operator[ ]

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 83

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Overloading on const
This lets you treat const and non-const objects differently:
String str{"World"}; // non-const String object
String const constStr{"Hello"}; // const String object
char c1{str[0]}; // fine — reading a
// non-const String
char c2{constStr[0]}; // fine — reading a
// const String
str[0] = 'x'; // fine — writing a
// non-const String
constStr[0] = 'x'; // error! — writing a
// const String
These differences in behavior would not occur if the two functions had the
same return type:
§ The use of const is crucial here, too

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 84

The Classic C++ Meanings of const


What does it mean for a member function to be const?
§ None of the memory inside the object is modified inside the function:
Æ This is bitwise constness
Æ This is what the compiler enforces
§ The object’s abstract state isn’t modified inside the function, although
its bits might be:
Æ This is conceptual constness
Æ The compiler has no clue about this

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 85

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Problems with Bitwise constness


A pointer may be inside an object, but what it points to is not:
struct String {
String(char const*value = 0); // the ctor makes data
// point to a copy of what
// value points to
operator char *() const { return data;}
private:
char *data;
};
String const s{"Hello"}; // create constant object
char *nasty{s}; // calls op char*() const
*nasty = 'M'; // modifies s.data[0]
std::cout << s; // writes "Mello"
This code is legal, but wrong. Bitwise constness is too lax here.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 86

Problems with Bitwise constness


Here’s a different implementation for the String class:
struct String {
String(const char *value = 0)
: lengthIsValid{false} { ... }
std::size_t length() const;
...
private:
char *data;
std::size_t dataLength; // last calculated length of string
bool lengthIsValid; // bit indicating if dataLength is valid now
};
Note the use of the cache for the string length.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 87

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Problems with Bitwise constness


But consider the implementation of the length function:
std::size_t String::length() const
{
if (!lengthIsValid) {
dataLength = std::strlen(data); // error!
lengthIsValid = true; // error!
}
return dataLength;
}
This code is right, but illegal. Here, bitwise constness is too strict.
We need a way to:
§ Implement conceptual constness
§ Circumvent the compiler’s insistence on bitwise constness

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 88

Enter mutable
Data members declared mutable may always be modified:
§ Even if inside a const member function or const object.
Implementing conceptual constness is thus straightforward:
§ Conceptually const member functions are declared const.
§ Data members that may need to change inside const member functions
are declared mutable.
Classes should be designed and implemented with support for conceptual
constness.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 89

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Using mutable
struct String {
String(char const*value = 0)
: lengthIsValid{false} { ... }
std::size_t length() const;
...
private:
char *data;
mutable std::size_t dataLength; // last calculated length of string

mutable bool lengthIsValid; // bit indicating if dataLength is


// currently valid
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 90

Using mutable
This is now legal:
std::size_t String::length() const
{
if (!lengthIsValid) {
dataLength = std::strlen(data); // now okay
lengthIsValid = true; // now okay
}
return dataLength;
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 91

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Casting Away const


Avoid it whenever possible:
§ “A cast is a sign that negotiations between you and your compiler have
broken down.”
§ Yields undefined results for objects that are truly const, i.e., are defined
const:
struct Widget {
void transmogrify(); // non-const function
...
};
Widget const cw;
cw.transmogrify(); // error!
const_cast<Widget&>(cw).transmogrify(); // undefined!
Widget const*pcw{new Widget};
pcw->transmogrify(); // error!
const_cast<Widget*>(pcw)->transmogrify(); // okay

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 92

Casting Away const


§ Useful for translating between const-correct and const-incorrect code:
void setName(char *pName); // *pName won’t change,
// but setName is in a
// const-incorrect library
// whose source code can’t
// be modified
inline // const-correct interface
void setName(char const* pName) // to above function
{
setName(const_cast<char*>(pName));
}
char const*name{"Darla"}; // in client code
setName(name); // calls library function via
// const-correct interface

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 93

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Make const Member Functions Thread-Safe


Recall the two classic takes on const member functions:
§ Bitwise const:
Æ Bits in current object (*this) don’t change.
Æ What compilers enforce.
§ Conceptually const:
Æ Current object doesn’t visibly change.
w Bits may change, but class interface doesn’t reveal it.
Æ What developers should implement.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 94

mutable
Disables bitwise const checking for “hidden” data members.
§ Common for caches:
struct Widget {

int magicValue() const


{
if (cacheValid) return cachedValue;
else {
cachedValue = expensiveComputation(); // write data mem
cacheValid = true; // write data mem
return cachedValue;
}
}

private:
mutable int cachedValue;
mutable bool cacheValid;

};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 95

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

const and Threading


Consider:
Widget w;

// Thread 1 // Thread 2
auto v1(w.magicValue()); auto v2(w.magicValue());
Each thread reads w.
§ I.e., calls a const member function.
§ Should be thread-safe: simultaneous reads.
§ Isn’t.
Æ Both “reads” may write w’s mutable members.
Æ Race! ⇒ UB!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 96

const and Threading


Nothing wrong with calling code.
§ const member functions ⇒ thread-safe!
Æ Assumed by C++11 standard library.
w const member functions may be called w/o synchronization.
Widget::magicValue is broken.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 97

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

The Modern C++ Meaning of const


What does it mean for a member function to be const in a Modern C++
context?
§ Because the Standard Library assumes that const members may be
called without synchronization:
§ Modern meaning of const is conceptual constness and thread-safe.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 98

Revised Code
struct Widget {

int magicValue() const


{
std::lock_guard<std::mutex> guard{m}; // lock mutex
if (cacheValid) return cachedValue;
else {
cachedValue = expensiveComputation();
cacheValid = true;
return cachedValue;
}
} // unlock mutex

private:
mutable std::mutex m; // mutex for cache
mutable int cachedValue;
mutable bool cacheValid;

};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 99

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Mutex not Always Required


Atomic data automatically synchronized.
struct Gadget {


int getWeight() const
{
++callCount; // thread-safe RMW op.
return wt;
}
private:
mutable std::atomic<unsigned> callCount;
int wt;
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 100

Atomic Data no Panacea


Transactions with >1 mutable members w/o mutex ⇒ danger:
struct Widget {

int magicValue() const // modified


{ // implementation
if (cacheValid) return cachedValue;
else {
auto v1(expensiveComputation1());
auto v2(expensiveComputation2());
cacheValid = true; // uh oh (part 1)
return cachedValue = v1 + v2; // uh oh (part 2)
}
}

private:
mutable std::atomic<int> cachedValue;
mutable std::atomic<bool> cacheValid;

};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 101

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Atomic Data no Panacea


Reversing “uh oh” lines also problematic:
struct Widget {

int magicValue() const


{
if (cacheValid) return cachedValue;
else {
auto v1(expensiveComputation1());
auto v2(expensiveComputation2());
cachedValue = v1 + v2;
cacheValid = true;
return cachedValue;
}
}
… // as before
};
Multiple threads can see cacheValid as false.
§ All will perform the expensive computations!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 102

Atomic Data no Panacea


Mutex solves the problem (and eliminates need for atomics):
struct Widget {

int magicValue() const


{
std::lock_guard<std::mutex> guard{m}; // lock m
if (cacheValid) return cachedValue;
else {
auto v1(expensiveComputation1());
auto v2(expensiveComputation2());
cacheValid = true;
return cachedValue = v1+v2;
}
} // unlock

private:
mutable std::mutex m;
mutable int cachedValue; // not atomic
mutable bool cacheValid; // not atomic

};
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 103

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Meaning of “Thread-Safe”
const member functions should be:
§ Bitwise const or
§ Internally synchronized.
Æ Typically via atomic data or mutex.

Exception: code guaranteed never to be used in a multithreaded context:


§ Classes used only in ST programs.
§ Classes where races on instances precluded by design.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 104

Guideline
Make const member functions thread-safe.

Based on EMC++ Item 16.


Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 105

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

The Lifetime of const


const applies only to fully constructed objects:
§ Inside their constructors and destructors, const objects aren’t:
Æ This allows data members to be initialized/destroyed
§ Example:
{
Widget const cw; // construct cw; during construction,
// cw may be modified
... // use cw; only const member functions
// may be invoked; cw’s conceptual
// state should never change
} // destroy cw; during destruction,
// cw may be modified
§ Within a ctor/dtor, there is no way to determine whether the object
being created/destroyed is const

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 106

The Lifetime of const


During construction of const objects, it’s thus possible to hand out non-
const pointers (i.e., pointers to non-const) to the object being constructed:
Widget *ngptw; // “nasty global ptr to Widget”;
// note that the Widget isn’t const
void ilf(Widget *pw) // “innocuous-looking function”
{
ngptw = pw; // save parameter in ngptw
}
Widget::Widget() // Widget ctor
{
...
ilf(this); // pass ptr to non-const
... // self to ilf
}
Widget const cw; // create a const object
ngptw->someNonConstFunc(); // ouch! modify it!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 107

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

The Lifetime of const


Moral: try to avoid giving out “handles” to non-const versions of the
current object during construction.
§ A pointer is a handle:
Æ Passing this as a pointer-to-non-const may be dangerous
§ A reference is a handle:
Æ Passing *this as a reference-to-non-const may be dangerous

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 108

Type Aliases
A type alias can be created with either the (classic) keyword typedef:
typedef Widget const* WCP;
or with the (modern) keyword using:
using WCP = Widget const*;
These are just different syntaxes to do exactly the same thing. The modern
approached is preferred because:
• The new syntax is more general:
• Supports templated aliases
• Better to be consistent than to use two different styles.
• The new syntax is easier to understand.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 109

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

const and Type Aliases


Combining const and type alises can be confusing:
typedef char* LPSTR; // from VC6-8’s <winnt.h>
// (more or less)
using LPSTR = char*; // equivalent meaning with
// modern syntax
const LPSTR s; // the type of s is char * const,
// NOT const char*!

// This clearer if we use the “const is always to the right” convention.


LPSTR const s; // the type of s is char * const,
// NOT char const*!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 110

const and Type Alisases


Here’s one way to reduce confusion:
§ Remember that const T and T const mean the same thing when T isn’t
a pointer
§ Never put const at the beginning of a declaration:
Æ Prefer T const to const T
Æ On the previous page, s would be defined like this:
LPSTR const s; // same meaning as before,
// but arguably clearer
Æ Other examples:
char const *pAnimal = "Wombat";
Widget const *pw = new Widget;
UPInt const operator+(UPInt const& lhs, UPInt const& rhs); [*]
This advice is sound, but the resulting declarations look strange to many
programmers.
[*] Note: returning const value for exposition only. Not modern practice.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 111

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

const, Type Aliases, and Arrays


When const is applied to an array, it is the array elements that become
const:
§ The array itself (e.g., its size and memory) is inherently unchangeable.
Hence:
typedef Wombat WArray[10]; // A WArray is an
// array of 10 Wombats
using WArray = Wombat [10]; // equivalent meaning with
// modern syntax
WArray const wildWombats; // wildWombats is an
// array of 10 const Wombats
Wombat *pw = &wildWombats[2]; // error! can’t convert
// Wombat const* to Wombat*

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 112

Type Aliases and Arrays


Type aliases for arrays lead to new/delete problems anyway:
using AddressLines = std::string[4]; // an address is
// an array of 4 strings
typedef std::string AddressLines[4]; // classic syntax
...
std::string *pa1{new AddressLines};
...
delete pa1; // undefined!
delete [ ] pa1; // fine
As a result, type aliases for arrays are probably best avoided.
§ In fact, arrays are best avoided. Alternatives include:
Æ std::vector
Æ std::array

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 113

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

consts, #defines, and Anonymous enums


There are three common ways to define constant values:
#define NUM_STATES 50 // macro
int const numStates{50}; // constant
enum { numStates = 50 }; // anonymous enum (“the enum hack”)
Macros are never worth using, because they don’t obey scope:
§ This pollutes the global namespace and can cause name collisions.
§ Can’t be declared private or protected in a struct or local to a function.
Enumerators of anonymous enums (“the enum hack”) are attractive:
§ All the features of macro constants, but they obey scope.
Æ Like #defines, they work with both C and C++ and use no memory.
const objects have their own features:
§ They have addresses, so pointers/references can refer to them.
§ Can be used for any type, not just integral types.
Æ For floating point types, they may generate better code than #defines.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 114

const and Optimizations


const can be cast away, so compilers typically can’t assume that const objects
are really constant.
§ The exception is when compilers can see that an object is defined const.
Æ Then they can optimize based on the object’s value never changing.
But proper use of const makes it possible to replace pass-by-value with pass-
by-reference-to-const.
§ We’ll later see that passing by reference-to-const is efficiency guideline #1.
void f(Widget w); // typically inefficient
// (w must be constructed)
void f(Widget const& w); // typically efficient, but
// requires const-correct code
Note that pass-by-reference-to-non-const isn’t as good:
Widget makeWidget();
void g(Widget& w); // like f, but without const
f(makeWidget()); // fine
g(makeWidget()); // error! can’t pass temp to ref-to-non-const

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 115

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

const Summary
§ Use const whenever possible.
§ Type conversions involving multiple levels of indirection may seem
counterintuitive.
Æ If compilers object to your code, they’re probably right.

§ Avoid pass-by-const-value in interfaces.


§ Return const objects when you want to emulate rvalue semantics, but
not if you want to enable move operations.
§ Implement conceptual constness in const member functions.
Æ mutable can help you do this.
Æ const requires thread-safe implementation
§ During construction and destruction, const objects aren’t
§ Be careful when combining const and pointer-based type aliases
§ Prefer anonymous enums and const objects to #defines.
§ The use of const enables some efficiency optimizations.
Based on EC++/3E Item 3.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 116

Exercise: Code Duplication and Overloading on const


Suppose String::operator[ ] did more than just return a reference:
struct String {
... // as before
char& operator[ ](int position)
{
… // do bounds checking, log data
// access, verify data integrity
return data[position];
}
char const& operator[ ](int position) const
{
… // do bounds checking, log data
// access, verify data integrity
return data[position];
}
};
How can we avoid duplicating common code in such functions?

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 117

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Range-based for Loops

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 118

Range-Based for Loops


Looping over a container can take this streamlined form:
std::vector<int> v;

for (int i : v) std::cout << i; // iteratively set i to every
// element in v
The iterating variable may also be a reference:
for (int& i : v) std::cout << ++i; // increment and print
// everything in v
auto, const, and volatile are allowed:
for (auto i : v) std::cout << i; // same as above
for (auto& i : v) std::cout << ++i; // ditto
for (int volatile i : v) someOtherFunc(i); // or "auto volatile i”
By default you should do this:
for (auto const& i : v) ~~~ // if you won’t modify
for (auto& i : v) ~~~ // if you do modify

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 119

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Range-Based for Loops


Valid for any type supporting the notion of a range.
§ Given object obj of type T,
obj.begin() and obj.end() or begin(obj) and end(obj) are valid.
Includes:
§ All C++11 library containers.
§ Arrays and valarrays.
§ Initializer lists.
§ Any UDT T where T.begin() and T.end() or begin(T) and end(T) yield
suitable iterators.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 120

Range-Based for Loops


Examples:
std::unordered_multiset<std::shared_ptr<Widget>> msspw;

for (auto const& p : msspw) {
std::cout << p << '\n'; // print pointer value
}

short vals[ArraySize];

for (auto& v : vals) { v = -v; }

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 121

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Range-Based for Loops


Range form valid only for for-loops.
§ Not do-loops, not while-loops.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 122

Introduction to
Rvalue References and Move Semantics

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 123

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Retronym
What are these?

Now what are they?

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 124

Retronym
Classic C++ had “references”:
§ Syntax is type&
§ Semantic is “alias”

Modern C++ renames these “lvalue references”…


§ Syntax is type& Meaning cannot change.
§ Semantic is “alias” That would break existing code!
and introduces a new reference type called “rvalue references”:
§ Syntax is type&&
Note that an rvalue reference is
§ Semantic is “alias to an rvalue” always an alias to an rvalues.

But an lvalue reference can alias either an lvalue or an rvalue.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 125

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Lvalues and Rvalues


The history of the names lvalue and rvalue come from a simple assignment
statement:
§ a = b;
§ In this statement
Æa must be an lvalue
Æb is treated like an rvalue (but may actually be an lvalue)
§ “lvalue” comes from “left side of assignment
§ ”rvalue” comes from “right side of assignment
May not be a helpful way to think of them, because lvalues can be const,
and so can’t be on the left side of an assignment.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 126

Lvalues and Rvalues


A better way of thinking about these terms:
Every variable can be thought of as being a container for a value. So we can
divide its properties into:
§ Its container properties
Æ name
Æ location in memory
Æ lifetime/scope

§ Its value properties


Æ the value that it holds
Æ the resources that it owns
In order to perform an assignment we need a container (an lvalue) and
something to put into it (an rvalue or an lvalue that we’ll treat as an
rvalue).

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 127

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Lvalues and Rvalues


Lvalues are objects with names and lifetimes.
Since they also have values, they can serve as rvalues (we can assign from
them).
But we don’t say they are rvalues. When we say something is an rvalue we
mean that it has two properties:
§ It is anonymous (it has no name)
§ It is temporary (it will be destroyed after this expression)

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 128

The Rvalue Opportunity


What are the implications of the two properties of an rvalue object?
§ It is anonymous (it has no name)
§ It is temporary (it will be destroyed after this expression)
No other objects depend on it (because they can’t express its name) and its
resources are about to be destroyed.
If an object is an rvalue, and it has one or more resources (ex: buffer of
memory) it represents an opportunity.
What is the opportunity?
We can move the resource instead of copying it!

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 129

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Rvalue Reference Parameters


Arguments taken by rvalue reference parameters
§ look like: type&&
§ will only bind to rvalues
Æ enforced by the compiler at compile time
§ allow the called code to remove resources
Æ unless const (type const&& is not useful)
§ may leave the object in the “moved-from” state
Æ can be assigned to
Æ can be destroyed
Æ no other operation can be assumed to be safe

struct Widget {
Widget(Widget const&); // copy constructor
Widget(Widget&&); // move constructor
Widget& operator=(Widget const&rhs); // copy assignment
Widget& operator=(Widget&&rhs); // move assignment
Kalb for NVIDIA Copyrighted material, all rights reserved.
http://cpp.training/ Slide 130

Rvalue Reference Binding


The compiler will only bind rvalues to rvalue reference parameters.
When overloaded on rvalue and lvalue reference parameters compiler
picks the correct one.

Widget MakeWidget(); // Widget factory


Widget a{MakeWidget()}; // move constructor; returns rvalue
Widget b{a}; // copy constructor; a has name

If not overloaded:
§ lvalue reference only:
Æ compiler will allow rvalue objects to bind to lvalue references
w safe: no stealing from lvalue objects
w backwards compatible
§ rvalue reference only:
Æ compiler will not allow lvalue objects to bind to rvalue references
w compile-time error instead of allowing stealing from lvalue objects

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 131

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Lvalues as Rvalues
In “naturally occurring code” an lvalue will never bind to an rvalue
reference parameter and so will never be moved from.
This is safe and backwards compatible.
But it isn’t always what we want.
Sometimes we want to move from an lvalue object. For this we have
std::move().

Widget a{MakeWidget()};
Widget b{std::move(a)}; // move constructor; “a” treated like rvalue

Burden on us to not do anything with moved-from objects except assign or


destroy (allow to go out of scope)

a.member_function(); // No! “a” is now moved-from object


a = Widget{}; // safe
By “naturally occurring code” I mean code without “std::move().”
All Classic C++ code is “naturally occurring code.”

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 132

Type fundamentals
Special member functions

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 133

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Constructors, Destructors, and Assignment Operators

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 134

Know What Functions C++ Silently


Writes and Calls
You say this:
struct Empty{};
You may get this:
struct Empty {

Empty(); // default ctor


Empty(Empty const& original); // copy constructor
Empty(Empty&& source) noexcept; // move constructor
~Empty() noexcept; // destructor
Empty& operator=(Empty const& rhs); // copy assignment operator
// move assignment operator
Empty& operator=(Empty&& source) noexcept;
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 135

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Implicitly-Generated Member Functions


§ All are inline functions
§ A default constructor is generated iff you declare no constructors at all
§ A copy constructor is generated iff you declare no copy constructor
§ These functions are generated iff needed, but it doesn’t take much to
need them:
Empty const e1; // default constructor,
// destructor
Empty e2{e1}; // copy constructor
Empty e2 = e1; // copy constructor (not assignment!)
Empty e2{std::move(e1)}; // move constructor
Empty e2 = std::move(e1); // move constructor (not assignment!)
e2 = e1; // copy assignment operator
e2 = std::move(e1); // move assignment operator

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 136

Implicitly-Generated Move Operations


Move constructor and move operator= are “special: ”
§ Generated by compilers under appropriate conditions.
Conditions:
§ All data members and base classes are movable.
Æ Implicit move operations move everything.
Æ Most types qualify:
w All built-in types (move ≡ copy).
w Most standard library types (e.g., all containers).
§ Generated operations likely to maintain class invariants.
Æ No user-declared copy or move operations.
w Custom semantics for any ⇒ default semantics inappropriate.
w Move is an optimization of copy.
Æ No user-declared destructor.
w Often indicates presence of implicit class invariant.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 137

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Destructors and Implicit Struct Invariants


struct Widget {

~Widget() { assert(sizeSum == v.size()+s.size()); }


...
private:
std::vector<int> v;
std::set<double> s;
std::size_t sizeSum;
};
If Widget had implicitly-generated move operations:
Widget MakeWidget();
{
Widget w{MakeWidget()}; // When temporary goes out of
// scope, assert fires!
~~~
}
User-declared dtor no compiler-generated move ops for Widget.

Scott Meyers, Software Development Consultant Kalb for NVIDIA © 2014 Scott Meyers, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 138

Implicitly-Generated Move Operations


Examples:
struct Widget1 { // copyable & movable type
explicit Widget1(std::u16string n);
private:
std::u16string name; // copyable/movable type
long long value; // copyable/movable type
}; // implicit copy/move ctor;
// implicit copy/move operator=

struct Widget2 { // copyable type; not movable


explicit Widget2(std::u16string n);
Widget2(Widget2 const& rhs); // user-declared copy ctor
private: // ⇒ no implicit move ops;
std::u16string name; // implicit copy operator=
long long value;
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA © 2014 Scott Meyers, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 139

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Custom Moving ⇒ Custom Copying


Declaring a move operation prevents generation of copy operations.
§ Custom move semantics ⇒ custom copy semantics.
Æ Move is an optimization of copy.
struct Widget3 { // movable type; not copyable
explicit Widget3(std::u16string n);
Widget3(Widget3&& rhs) noexcept; // user-declared move ctor
// ⇒ no implicit copy ops;
Widget3& // user-declared move op=
operator=(Widget3&& rhs) noexcept; // ⇒ no implicit copy ops
private:
std::u16string name;
long long value;
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA © 2014 Scott Meyers, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 140

Implicit Copy Operations Revisited


Rules for implicit copy operations can lead to trouble:
struct ProblemSince1983 { // copyable struct
~ProblemSince1983() { delete p; } // implicit invariant:
// p owns *p
... // no copy ops
// declared
private:
int *p;
};
{ // some scope
ProblemSince1983 prob1;
...
ProblemSince1983 prob2(prob1);
...
} // double delete!

Scott Meyers, Software Development Consultant Kalb for NVIDIA © 2014 Scott Meyers, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 141

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Implicit Copy Operations Revisited


Ideally, rules for copying would mirror rules for moving, i.e.,
§ Declaring a custom move op ⇒ no implicit copy ops.
Æ Already true.
§ Declaring any copy op ⇒ no implicit copy ops.
Æ Too big a change for C++11.
§ Declaring a destructor ⇒ no implicit copy ops.
Æ Too big a change for C++11.
However:
§ Implicit copy ops deprecated in structs with user-declared copy, move,
or dtor operations.
Æ Compilers may issue warnings.

Scott Meyers, Software Development Consultant Kalb for NVIDIA © 2014 Scott Meyers, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 142

Implicit Copy Operations Revisited


struct ProblemSince1983 { // as before
~ProblemSince1983() { delete p; }
... // no copy ops
private:
int *p;
};
{ // as before
ProblemSince1983 prob1;
...
ProblemSince1983 prob2(prob1); // generation of copy
// ctor deprecated
...
} // still double delete

Scott Meyers, Software Development Consultant Kalb for NVIDIA © 2014 Scott Meyers, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 143

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Default Behavior
Default constructor:
inline Empty::Empty(){}
Destructor:
inline Empty::~Empty() noexcept {}
§ The function is nonvirtual unless:
Æ it’s in a derived class and
Æa base class’s destructor is virtual
§ The destructor is noexcept unless
Æa base class’s destructor is noexcept(false) or
Æa data member’s destructor is noexcept(false)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 144

Default Copy/Move Construction and Assignment


The rule is “memberwise” construction/assignment:
§ Built-in types undergo bitwise copy/move.
§ If a member is of a struct with a copy/move constructor or a copy/move
assignment operator, that struct’s function is called
Æ This proceeds recursively

§ If a base class has a copy/move constructor or copy/move assignment


operator, that class’s function is called
Æ This also proceeds recursively

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 145

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Default Copy Construction and Assignment


In general, “the right thing” happens:
§ If you write a copy/move constructor or copy/move assignment
operator, it is called when it should be
§ If you don’t write a copy constructor or copy assignment operator, you
get bitwise copy by default
§ If you don’t write a move constructor or move assignment operator,
you get “memberwise” moves (either copy or call of move operation)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 146

An Example
struct NamedInt {
NamedInt(char const*name, int value);
NamedInt(std::string const& name, int value);
...
private:
std::string nameValue; // a data member of struct type
int intValue; // a data member of built-in type
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 147

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

An Example
The compiler will not generate a default constructor for NamedInt.
§ A constructor already exists.
It will generate a copy constructor and copy assignment operator (if
needed).
Consider:
NamedInt i{"Smallest Prime Number", 2};
NamedInt j{i}; // calls copy constructor
§ j.nameValue will be initialized by calling the string copy constructor
§ j.intValue will be initialized by bitwise copy
NamedInt k{std:move(i)}; // calls move constructor
§ k.nameValue will be initialized by calling the string move constructor
§ k.intValue will be initialized by bitwise copy

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 148

Default Copy Assignment Operators: The Fine Print


No copy assignment operator is generated for classes:
§ Containing a non-static const data member:
Æ It’s not legal to assign to a const data member!
§ Containing a non-static reference data member:
Æ References can only be initialized, never assigned to
§ Containing data members with an inaccessible copy assignment
operator.
Æ The compiler-generated copy assignment operator wants to assign to
all members.
§ Inheriting from a class with an inaccessible copy assignment operator.
Æ All members includes the inherited ones!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 149

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Default Move Assignment Operators: The Fine Print


No move assignment operator is generated for classes:
§ Containing a const member:
Æ It’s not legal to assign to a const member!
§ Containing a reference member:
Æ References can only be initialized, never assigned to
§ Containing members with an inaccessible copy/move assignment
operator.
Æ The compiler-generated move assignment operator wants to assign
to all members.
§ Inheriting from a class with an inaccessible copy/move assignment
operator.
Æ All members includes the inherited ones!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 150

Default Assignment Operators: The Fine Print


Example:
struct NamedInt {
NamedInt(char const*name, int value);
... // assume no operator=
// is declared
private:
std::string const nameValue; // this is now const
int intValue;
};

NamedInt ni1{"Nancy", 1};


NamedInt ni2{"Scott", 2};
ni1 = ni2; // error! can’t generate
// operator= for classes
// with const members

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 151

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

default Member Functions


As of C++11 we can declare special member functions to have “default”
implementation.
Generated versions are:
§ Public
§ Inline
§ Non-explicit
defaulted member functions have:
§ User-specified declarations with the usual compiler-generated
implementations.

Scott Meyers, Software Development Consultant Kalb for NVIDIA © 2014 Scott Meyers, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 152

default Member Functions


Typical use: “unsuppress” implicitly-generated functions:
struct Widget {

Widget(Widget const&); // copy ctor prevents implicitly-


// declared default ctor and
// move ops
Widget() = default; // declare default ctor, use
// default impl.
Widget(Widget&&) noexcept = default; // declare move ctor, use
// default impl.
...
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA © 2014 Scott Meyers, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 153

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

default Member Functions


Or change “normal” accessibility, explicitness, virtualness:
struct Widget {
virtual ~Widget() = default; // declare as virtual
explicit Widget(Widget const&) = default; // declare as explicit
private:
Widget& operator=(Widget&&) noexcept = default; // declare as private
...
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA © 2014 Scott Meyers, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 154

Guideline
Know what functions C++ silently writes and calls.

Based on EC++/3E Item 5.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 155

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Explicitly Disallow Use of Implicitly Generated


Member Functions You Don’t Want
Consider a class for Pascal-like arrays:
§ You might allow optional bounds checking
§ You might allow user-defined upper and lower bounds
§ You might allow true pass-by-value
Here’s a skeleton:
template<class T>
struct PascalArray {
PascalArray( /* arguments */ );
~PascalArray();
...
private:
...
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 156

Disallowing Assignment
Assignment is not allowed for built-in arrays:
char string1[10];
char string2[10];
string1 = string2; // error!
Assume you want to maintain this restriction:
PascalArray<int> intArray1( /* arguments */ );
PascalArray<int> intArray2( /* arguments */ );
intArray1 = intArray2; // should be an error
§ Usually, if you don’t want to provide some functionality, you just don’t
declare the function
§ This won’t work for operator=, because C++ will generate the function
automatically

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 157

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Declaring Assignment Private


Classic C++ solution: declare the function private:
template<class T>
struct PascalArray {
private:
PascalArray<T>& operator=(PascalArray<T> const& rhs);
...
};
This is good, but not perfect:
§ Members and friends can still make assignments
§ To prevent that, don’t define the function. Uses of operator= will then
generate link-time errors
§ This trick was traditionally used in the iostream library to prevent
users from accidently passing streams by value:
Æ That requires disallowing use of the copy constructor.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 158

delete Functions
The Classic C++ approach to disallowing functions worked, but was a “hack”
to work around a language limitation.
That limitation is addressed in Modern C++ with deleted functions.
deleted functions are the Modern C++ alternative to the Classic C++ approach
for disallowing special functions.
This techniques is more powerful and more general than the Classic C++
approach.

Kalb for NVIDIA © 2014 Scott Meyers, all rights reserved.


http://cpp.training/ Slide 159

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

delete Functions
deleted functions are “defined,” but can’t be used.
§ Most common application: prevent object copying:
struct Widget {
Widget(Widget const&) = delete; // declare and
Widget& operator=(Widget const&) = delete; // make uncallable

};
§ Note that Widget isn’t movable, either.
w Declaring copy operations suppresses implicit move operations!
w It works both ways:
struct Gadget {
Gadget(Gadget&&) = delete; // these also
Gadget& operator=(Gadget&&) = delete; // suppress copy
… // operations
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA © 2014 Scott Meyers, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 160

delete Functions
Not limited to member functions.
§ Another common application: control argument conversions.
Æ deleted functions are declared, hence participate in overload
resolution:
void f(void*); // f callable with any ptr type
void f(char const*) = delete; // f uncallable with char [const]*
auto p1(new std::list<int>); // p1 is of type std::list<int>*
extern char *p2;

f(p1); // fine, calls f(void*)
f(p2); // error! F(char const*) unavailable
f("Modern C++"); // error!
f(u"Modern C++"); // fine (char16_t* ≠ char*)

Scott Meyers, Software Development Consultant Kalb for NVIDIA © 2014 Scott Meyers, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 161

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guideline
Explicitly disallow use of implicitly generated member functions you don’t
want.

Based on EC++/3E Item 6.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 162

List Members in an Initialization List


in Declaration Order
This version of PascalArray supports user-definable bounds:
template<class T>
struct PascalArray {

PascalArray(int lowBound, int highBound);


...
private:
std::vector<T> data; // array data
int lowerBound; // logical lower index
};
template<class T>
PascalArray<T>::PascalArray(int lowBound, int highBound):
lowerBound{lowBound}, data(highBound - lowerBound + 1) {}
Question: How many elements does data hold?
Answer: You don’t know.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 163

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Object Initialization and Destruction


Data members are always initialized in the order of their declaration:
§ Otherwise it would be hard to guarantee that destruction order is the
inverse of construction order:
struct Wacko {
Wacko(char const*s): s1{s}, s2{"Hi"} {}
Wacko(Wacko const& x): s2{x.s1}, s1{"Mom"} {}
private:
std::string s1, s2;
};
Wacko w1{"Hello world!"};
Wacko w2{w1};
§ Making the initialization order the declaration order avoids the need
for per-object bookkeeping

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 164

Object Initialization and Destruction


Related points:
§ Static data members follow different rules:
Æ They are initialized only once per program run
§ Base class parts of derived class objects are also initialized in the order
of their declaration:
struct Vehicle { ... };
struct Asset { ... };
struct CompanyCar: Vehicle, Asset {
CompanyCar(VehicleID vid, CompanyID cid)
: Asset{cid}, Vehicle{vid} { ... };
...
};
Æ The rules are different for virtual base classes

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 165

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guideline
List members in an initialization list in declaration order.

Based on EC++/3E Item 4.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 166

Handle Assignment to Self in operator=


This is an assignment to self:
struct Widget { ... };
Widget w;
w = w; // w is assigned to itself
Often it doesn’t look so silly:
container[i] = container[j]; // assignment to self if i == j
*px = *py; // assignment to self if px == py
This is aliasing: having two or more names for the same object.
§ More on that later.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 167

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Handle Assignment to Self in operator=


Why bother to check for assignment to self?
§ Correctness: assignment to self can break “obvious” assignment
algorithms.
§ Efficiency: assignment can be expensive, and assignment to self is
superfluous.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 168

An Incorrect operator=
Consider:
struct Bitmap { … };
struct Widget {

private:
Bitmap *pb; // ptr to a heap-allocated
}; // object
This looks reasonable:
Widget& Widget::operator=(Widget const& rhs) // unsafe operator= impl.
{
delete pb; // get rid of old bitmap
pb = new Bitmap{*rhs.pb}; // take copy of new bitmap
return *this;
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 169

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Problems with “Obvious” Assignment Algorithms


You say:
Widget w{ params };
w = w; // same as w.operator=(w)

This is the situation inside operator=:


:Bitmap

pb
*this
rhs

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 170

Problems with “Obvious” Assignment Algorithms


Recall the body of operator=:
delete pb;
pb = new Bitmap{*rhs.pb};
return *this;
After the first statement, we have this:
???

pb
*this
rhs

From here on out, everything is undefined!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 171

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Checking for Assignment to Self


A time-honored solution is to check for assignment to self:
Widget& Widget::operator=(Widget const& rhs)
{
if (this == &rhs) return *this; // ignore self-assignment
delete pb;
pb = new Bitmap{*rhs.pb};
return *this;
}
This handles self-assignment, but it’s not exception-safe:
§ Suppose this != &rhs, but “new Bitmap” throws an exception….

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 172

Using Resource-Managing Objects


Using an object to manage the Bitmap resource solves both problems:
struct Widget {

private:
std::shared_ptr<Bitmap> pb;
};
Widget& Widget::operator=(Widget const& rhs) // safe operator= impl
{
pb.reset(new Bitmap{*rhs.pb}); // make pb point to a
// copy of rhs’s Bitmap
// iff the copy succeeds
return *this;
}
Making operator= exception-safe usually makes it self-assignment-safe, too.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 173

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Careful Function Implementation


Some resource-managing classes incur overhead.
§ std::shared_ptr does.
If the overhead is unacceptable, consider:
§ Raw pointer data members + careful implementation of operator=.
Æ Both self-assignment-safety + exception-safety can often be achieved:
struct Widget {

private:
Bitmap *pb; // back to a raw pointer
};
Widget& Widget::operator=(Widget const& rhs)
{
Bitmap *pNew{new Bitmap{*rhs.pb}}; // copy Bitmap
delete pb; // delete original Bitmap
pb = pNew; // set pb to copy
return *this;
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 174

Using Copy-and-Swap
A general approach to self-assignment-safe and exception-safe operator= is
based on a non-throwing swap member function:
struct Widget {

void swap(Widget& rhs) noexcept; // swap *this’s and rhs’s data in a
}; // nonthrowing operation
§ Many classes offer swap, e.g., all STL containers.
Æ It’s a common convention.
operator= can then be implemented via “copy and swap”:
Widget& Widget::operator=(Widget const& rhs)
{
Widget temp{rhs}; // copy rhs to temp
swap(temp); // swap *this’s and temp’s data
return *this; // destroy temp (and *this’s old data)
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 175

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Using Copy-and-Swap
Copy-and-swap is nice, but it always makes a copy:
§ A careful non-copying implementation can sometimes be more
efficient.
§ But copy-and-swap is a recognizable idiom.
Æ That improves maintainability.
Æ It’s also usually easier to get right.
w And to keep right as the function is maintained.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 176

Back to Aliasing
As noted before, aliasing can lead to unobvious assignment to self. But
operator= is not the only function that must worry about aliasing:
struct Base {
void mf1(Base& rb); // rb and *this could
// be the same
...
};
void f1(Base& rb1, Base& rb2); // rb1 and rb2 could
// be the same
struct Derived: Base {
void mf2(Base& rb); // rb and *this could
// be the same
...
};
int f2(Derived& rd, Base& rb); // rd and rb could be
// the same

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 177

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guidelines
Handle assignment to self in operator=.
§ Writing exception-safe code often handles it automatically.
§ Aliasing may also arise in other situations.

Based on EC++/3E Item 11.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 178

Assign to All Data Members in operator=


It would be nice to selectively override the default operator=:
§ Handle pointers manually
§ Let the compiler handle most everything else
This isn’t possible. You are responsible for everything inside operator=.
§ By writing your own operator=, you tell the compiler you don’t like
something about the version it would write:
Æ There is no way to specify what you don’t like
Æ Hence you must write everything

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 179

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Assign to All Data Members in operator=


Example:
struct String {

String& operator=(String const& rhs);


...
size_t length() const;
private:
char *data;
mutable size_t dataLength;
mutable bool lengthIsValid;
};
String& String::operator=(String const& rhs)
{
if (this == &rhs) return *this;
char *newData{new char[rhs.length() + 1]};
strcpy(newData, rhs.data);
delete [ ] data;
To make this thread
data = newData;
dataLength = rhs.dataLength; safe, we’d need a
lengthIsValid = rhs.lengthIsValid; mutex.
return *this;
}
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 180

Assign to All Data Members in operator=


This is easy when the class is written, but:
§ During class maintenance, you may add new data members
§ When you do, you must remember to update all operator=s:
Æ The compiler will probably not remind you if you forget
§ Ditto for the copy constructor and move operations (ctr & operator=)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 181

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Inheritance and Assignment


All the class data members includes those in base classes!
struct B {
public:
B(int initialValue): x{initialValue} {}
private:
int x;
};
struct D: B {
public:
D(int initialValue)
: B{initialValue}, y{initialValue} {}
D& operator=(D const& rhs);
Not needed.
private:
int y;
};
D::operator= must assign to B::x!
In this example, the default copy
and move ops are just fine.
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 182

Inheritance and Assignment


This is an incomplete assignment:
D& D::operator=(D const& rhs)
{
if (this == &rhs) return *this;
y = rhs.y;
return *this; // B::x not assigned
}
And this is illegal:
D& D::operator=(D const& rhs)
{
if (this == &rhs) return *this;
x = rhs.x; // error!
y = rhs.y;
return *this;
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 183

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Inheritance and Assignment


This works:
D& D::operator=(D const& rhs)
{
if (this == &rhs) return *this;
B::operator=(rhs); // call this->B::operator=
y = rhs.y;
return *this;
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 184

Aside: Using typedefs (aliases) for Base Classes


Rather than hard-coding base class names into member functions, some
people prefer to use a class-specific type alias for the base class:
struct D: B {
using Parent = B; // “Parent” is the base class; note
... // that the type alias is private
public:
...
};
D& D::operator=(D const& rhs)
{
if (this == &rhs) return *this;
Parent::operator=(rhs); // call op= in the
// parent (base) class
y = rhs.y;
return *this;
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 185

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Inheritance and Copy Construction


Copy constructors are analogous to assignment operators:
struct B {
B(); // default constructor
B(B const& rhs); // copy constructor
...
};
struct D: B {
D(D const& rhs);
...
};
D::D(D const& rhs) // must invoke B::B() on base
{ // class part!
...
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 186

Inheritance and Copy Construction


The solution is to pass an appropriate argument to the base class in the
derived class’s constructor’s member initialization list:
D::D(D const& rhs) // invokes B::B(B const&) on
: B{rhs} // base class part
{
...
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 187

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Inheritance and Assignment


Consider:
B *pb1{new D};
B *pb2{new D};
*pb1 = *pb2;

pb1 pb2

B B
=
D D

What happens here?

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 188

Inheritance and Assignment


Can’t we make the assignment operator virtual to solve this problem?
That doesn’t fix the problem. Consider that we may have more than one
type of derived class, D1 and D2, of different sizes.
B *pb1{new D1};
B *pb2{new D2}; // D2 requires more storage
*pb1 = *pb2;

pb1 pb2

B B
=
D1 D2

There is no solution other than to prevent this at compile time.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 189

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Inheritance and Assignment


Why does this problem happen?
In the Object-Oriented Programming paradigm we use pointers of type
pointer-to-base-class which are assumed to be pointers-to-an-unspecified-
derived-class.
This works fine when we only dereference the pointers to call virtual
functions.
If we dereference a pointer expecting to find an actual base class objects,
which is what we did in this example, we’ll be disappointed.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 190

Copying Objects in OOP


Not being able to copy objects in an OOP hierarchy isn’t a real hardship. In
the OOP paradigm, we are rarely concerned about copying objects. We
usually just need to copy pointers.
When objects need to be copied, we use a virtual clone() function.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 191

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Preventing Copying of Objects in OOP


Scott developed and documented a solution that prevents objects in an
OOP hierarchy from being inappropriately copied.
Although his goal was to prevent a particular implementation detail from
biting us, his solution makes for a great design goal.
Read Item 33 in More Effective C++ for all the reasoning behind this
recommendation. Here, we’ll just present the final recommendation.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 192

Base Class vs Leaf Classes


The first step is to identify every class in your OOP hierarchy as either a
base class (intended to be derived from) or a leaf class (intended to be
instantiated).
You may currently have a class that is currently serving in both roles. It is
instantiated and used as a base class for another class.
Change that. Refactor that class into two classes.
What is common to it and the class or classes that is(are) derived from it
goes into one class that is a base class that is not to be instantiated.
What is unique to that class goes into a new class derived from the old
class, that is to be instantiated.
It is okay to have a base class derived from another base class, as long as it
isn’t a concrete class.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 193

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Base Class
Make every base class an abstract class by giving it at least one pure virtual
function.
Make its copy and move assignment operators protected.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 194

Leaf Class
Make every leaf class an concrete class that cannot be derived from.
Override each pure virtual function—so it can be instantiated.
Declare the class final—so that it can’t be derived from.
Make its copy and move assignment operators public.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 195

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Base Class
struct Animal // abstract base class
{
Animal() = default; // public ctors as needed
Animal(Animal&&) = default;
virtual ~Animal() = 0; // pure virtual
protected: // copy/move assign ops
Animal& operator=(Animal const&) = default; // implemented, but
Animal& operator=(Animal&&) = default; // protected
};
inline Animal::~Animal() = default; // dtor is pure, but an
// implementation is
// provided

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 196

Leaf Class & Results


struct Lizard final: Animal // publicly derived from base class
{ // and declared final
~Lizard() = default; // implement all inherited pure
}; // virtual functions
// Note that compiler provided copy operations do the right thing
// but, moves are suppressed because of the destructor (in this example).
Results:
Animal* pA1{new Lizard};
Animal* pA2{new Lizard};
*pA1 = *pA2; // Will not compile!
Lizard l1;
Lizard l2;
l1 = l2; // Compiles and works correctly.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 197

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guideline
Scott’s solution, intended to solve a nasty implementation detail, results in
a valuable design guideline.
Classes should be used as bases or concrete classes, not both.

Look at Item 33 in MEC++ for a discussion of this question.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 198

Guidelines
Assign to all data members in operator=.
Invoke base class copy/move constructors from derived class copy/move
constructors.
Make non-leaf classes abstract and leaf classes final.

Based on EC++/3E Item 12 and MEC++ Item 33.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 199

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Move Semantics and Perfect Forwarding


Best Practices

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 200

Assume that Move Operations are Not Present,


Not Cheap, and Not Used
Move semantics arguably the premier C++11 feature.
§ Some C++98 copy ops ⇒ C++11 move ops.
Æ Moves never costlier than copies, often less so.
§ Wide support in standard library.
Æ Moving containers usually much faster than copying.
§ An important optimization.
§ Not an efficiency panacea.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 201

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Move Operations
Not supported by all types:
§ All types created before 2011 and many types created since then.
Æ Exception: types w/ compiler-generated move functions.
w Classes w/o destructor/copy/move function declarations.

“Limited” support by some types:


§ std::array
Æ Moving std::array ⇒ each element moved.
w O(n), but cheaper than copying for move-optimized elements.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 202

Move Operations
When supported, not always faster than copying:
§ Small std::strings using SSO.
Æ For small capacities, move ≡ copy.
w “Small” often 7 or 15 characters.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 203

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Move Operations
Generally restricted to rvalues:
§ Copying lvalues generally requires copying. Lvalues
Æ Moving generally not permitted.
Æ Note that result of std::move is rvalue.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 204

Move Operations
Not always used. Consider:
std::string const stringValue("This string has 29 characters");
struct Widget {
Widget(): s{stringValue} {}
… // copy/move operations
private:
std::string s;
};
std::vector<Widget> vw;
Widget w;
for (std::size_t i{0}; i < n; ++i) { // append n copies
vw.push_back(w); // of w to vw
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 205

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Performance of n push_backs

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 206

Exception Safety of std::vector::push_back


C++98:
§ Strong guarantee.
C++11:
§ Strong guarantee.
Æ Avoids breaking legacy code.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 207

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Recap
Move semantics (i.e., using move ops instead of copy ops) no help if:
§ Copy source is lvalue.
§ Type offers no move support.
§ Move no cheaper than copying.
§ Move unusable (i.e., move ops not noexcept).

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 208

Assume…
If, for some expression, you know
§ Types involved support cheap move operations and
§ Expression uses move operations
assumptions unnecessary.
§ Expression employs cheap move operations.
Æ Move ops vs. copy ops part of overload resolution.
w No compiler discretion.
Don’t know information above?
§ Assume the worst.
Æ Typically true for template code.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 209

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guideline
Assume that move operations are not present, not cheap, and not used.

Based on EMC++ Item 29.


Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 210

Distinguish Universal References


from Rvalue References
Crux:
§ Rvalue reference type&&
§ type&& rvalue reference

This is a lie.
A useful lie.
For many purposes, the truth (reference collapsing) simply confuses.
§ We’ll cover the truth later.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 211

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

The Double Life of type&&


void f(Widget&& param); // rvalue reference
Widget&& var1{Widget{}}; // rvalue reference
auto&& var2 (var1); // not rvalue reference
template<class T>
void f(std::vector<T>&& param); // rvalue reference
template<class T>
void f(T&& param); // not rvalue reference

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 212

The Double Life of type&&


In “type&&”, “&&” means either:
§ Rvalue reference.
Æ Binds rvalues only.
Æ Facilitates moves.
New Terminology!

§ Universal reference
Æ Rvalue reference or lvalue reference.
w Syntactically type&&, but semantically type& or type&&.
Æ Bindslvalues and rvalues, const and non-const—everything!
Æ May facilitate copies, may facilitate moves.
Æ Same as Forwarding Reference.

Newer Terminology!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 213

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Shorthands
§ LRef = Lvalue reference
§ RRef = Rvalue reference
§ URef = Universal reference

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 214

In a Nutshell
URefs arise in two contexts:
§ Function template parameters:
template<class T>
void f(T&& param);
§ auto declarations:
auto&& var(…);

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 215

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

In a Nutshell

If a variable or parameter has declared type


T&&
for some
deduced type T,
it’s a universal reference.

Otherwise it’s an rvalue reference.

(This is part of the useful lie.)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 216

In a Nutshell
URefs require initializers:
§ Initializer for URef is lvalue ⇒ URef becomes LRef.
§ Initializer for URef is rvalue ⇒ URef becomes RRef.

Photo: Sid Mosdell

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 217

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Examples
template<class T>
void f(T&& param); // URef: proper syntax + deduced type
Widget w;
f(w); // w is lvalue URef becomes LRef;
// f(Widget&) instantiated
f(std::move(w)); // std::move yields rvalue
// URef becomes RRef;
// f(Widget&&) instantiated
f(Widget{}); // Widget{} yields rvalue
// URef becomes RRef;
// f(Widget&&) instantiated

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 218

Examples
std::vector<int> v;

auto&& val(10); // 10 is rvalue URef becomes RRef;
// val’s type is int&&
auto&& element(v[5]): // v[5] returns int& and
// LRefs are lvalues
// v[5] is lvalue
// URef becomes LRef;
// element’s type is int&

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 219

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

auto&&?
Uncommon in user code, but the basis of range-based for. Per C++11’s §6.5.4,
for ( for-range-declaration : expression ) statement
equivalent to
{
auto && __range = range-init;
for ( auto __begin(begin-expr), __end(end-expr);
__begin != __end;
++__begin ) {
for-range-declaration = *__begin;
statement
}
}

Allows __range to bind to lvalues or rvalues, const or non-const.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 220

¬Type Deduction ¬URef


void f(Widget&& w); // undeduced type RRef
template<class T>
void f(T&& param); // deduced type URef
template<class T>
struct Gadget1 {
Gadget1(Gadget1&& rhs); // undeduced type RRef
};
template<class T1>
class Gadget2 {
Gadget2(Gadget2&& rhs); // undeduced type RRef
template<class T2>
Gadget2(T2&& rhs); // deduced type URef
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 221

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

¬Type Deduction ¬URef


Not all T&&s in templates are URefs:
template<class T, // from C++11
class Allocator=allocator<T>> // standard
struct vector {

void push_back(T&& x); // RRef! T comes from vector<T>,
… // not arg passed to push_back
};

std::vector<int> vi;

vi.push_back(10.5); // arg type is double, but
// parameter type is int&&

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 222

push_back vs. emplace_back


Contrast with emplace_back:
template<class T,
class Allocator=allocator<T>>
struct vector { // from C++11

template<class... Args>
void emplace_back(Args&&... args); // URef! Args deduced

};

std::vector<int> vi;

vi.emplace_back(10.5); // arg type is double,
// parameter type is double&&

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 223

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

push_back vs. emplace_back


Note overloading (and lack thereof):
template<class T, class Allocator=allocator<T>>
struct vector {

void push_back(T const& x); // LRef (copy lvalues)
void push_back(T&& x); // RRef (move rvalues)
template<class... Args>
void emplace_back(Args&&... args); // URef (forward
… // everything)
};
§ emplace_back example motivates term forwarding reference.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 224

Syntax Matters
URefs must have form type&& where type is entirely deduced.
template<class T>
void f(T&& param); // URef
template<class T>
void f(T const&& param); // RRef (const not deduced)
template<class T>
void f(std::vector<T>&& param); // RRef (std::vector not
// deduced)
Widget w;
auto&& v1(w); // URef LRef
auto const&& v2(w); // RRef; won’t compile

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 225

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Syntax Matters
Realm of artistic freedom:
§ Parameter name
§ White space :-)
template<class ParamType>
void f(ParamType&& param); // URef
template<class ParamType>
void f(ParamType && param); // URef
auto &&I_did_it_my_way(44); // URef

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 226

Guideline
Distinguish universal references from rvalue references.

Based on EMC++ Item 24.


Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 227

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Pass and Return Rvalue References via std::move,


Universal References via std::forward
An RRef is definitely bound to an object that may be moved.
struct Widget {
Widget(Widget&& rhs); // rhs is bound to object
… // eligible for moving
};
A URef might be bound to an object that may be moved.
struct Widget {

template<class T>
void setName(T&& newName); // newName might be bound to
}; // object eligible for moving

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 228

Parameters are Lvalues


Parameters are lvalues.
§ Including RRefs and URefs.
Æ Rvalueness/lvalueness orthogonal to type.
Widget::Widget(Widget&& rhs); // RRef rhs is lvalue
template<class T>
void Widget::setName(T&& newName); // URef newName is
// lvalue

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 229

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Move Semantics Should be Enabled


Functions should have chance at move semantics.
§ I.e., to distinguish lvalue/rvalue arguments.
doSomething( argument ); // doSomething should be able to
// treat lvalue/rvalue args
// differently

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 230

Rvalue Reference ⇒ std::move


std::move unconditionally casts to rvalue.
§ Logical for RRef parameters.
Æ They are bound to rvalues.
struct Widget {
Widget(Widget&& rhs) // RRef ⇒ std::move
: name{std::move(rhs.name)},
p{std::move(rhs.p)}
{…}

private:
std::string name;
std::shared_ptr<SomeDataStructure> p;
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 231

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Universal Reference ⇒ std::forward


std::forward conditionally casts to rvalue.
§ Yields rvalue only if rvalue bound.
§ Logical for URef parameters.
Æ They might be bound to rvalues.
struct Widget {
template<class T>
void setName(T&& newName) // URef ⇒
{ name = std::forward<T>(newName); } // std::forward

};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 232

Universal Reference ⇒ std::forward


std::move + URef ⇒ disaster.
§ Casts to rvalue, even if lvalue is bound!
struct Widget {
template<class T>
void setName(T&& newName) // URef
{ name = std::move(newName); } // compiles, but is
… // wrong, wrong, wrong!
};
Widget w;
std::string wName{getWidgetName()};
w.setName(wName); // moves wName into w
… // wName == "" (probably)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 233

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

std::move_if_noexcept
Replaces std::move when all following true:
§ C++98 code offers strong guarantee.
§ Replacing C++98 copy with C++11 move offers lesser guarantee.
Æ I.e., copy→move optimization reduces exception guarantee.
§ Knowing move won’t throw ⇒ strong guarantee possible.

Never replaces std::forward.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 234

Refining the Rule


Pre-move processing typically needs RRefs/URefs to act like lvalues:
auto now([]{ return std::chrono::system_clock::now(); });
template<class T>
void setSignText(T&& text)
{
sign.setText(text); // pre-move work here
signHistory.add(now(),
std::forward<T>(text)); // if rvalue, move
} // here

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 235

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Refining the Rule


Hence:
§ Multiple uses of RRef param ⇒ only last one uses std::move.
§ Multiple uses of URef param ⇒ only last one uses std::forward.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 236

std::move, std::forward, and return


Rule applies to returns, too.
Consider operator+ for matrices:
§ Resources from rvalue parameters reusable in return value:
Matrix operator+(Matrix&& temp, Matrix const& y)
{
temp += y;
return std::move(temp); // move temp into return value
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 237

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

std::move, std::forward, and return


Without std::move, temp would be copied:
Matrix operator+(Matrix&& temp, Matrix const& y)
{
temp += y;
return temp; // copy temp into return value
}
Applying std::move therefore preferable.

Example due to David Abrahams.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 238

std::move, std::forward, and return


Story analogous for URefs:
template<class T>
Fraction // by-value return
reduceAndCopy(T&& frac) // universal reference param
{
frac.reduce();
return std::forward<T>(frac); // move rvalue into return
} // value, copy lvalue
No std::forward ⇒ frac unconditionally copied to return value.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 239

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Local Objects and return


For local objects, applying std::move in return a pessimization!
§ Compiler permitted to elide copy/move of local to return value.
§ Not so for function return values (e.g., from std::move).
Widget f1()
{
Widget w;

return w; // good: move of w may be elided
}
Widget f2()
{
Widget w;

return std::move(w); // bad: move of w may not be elided!
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 240

Copy Elision
The compiler is allowed to elide copies where results are “as if” copies were
made.
Return Value Optimization (commonly called RVO) is one such instance.
§ Caller allocates space on stack for return value, passes the address to callee
§ Callee constructs result directly in that space

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 241

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Return Value Optimization


How many parameters are passed to the function f?
std::string f()
{
std::string a("A");
int b{23};

return a;
}

C++ programmers answer “None,”


but assembly language programmers answer “One.”
Why?

The function is passed the address where the results should be written.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 242

Return Value Unoptimized


std::string f()
b
b:23
{
locals std::string a{"A"};
a:"A"
a int b{23};

address of return a;
parameters ?
&x
return
value }
f()
void g()
{
locals x:"A"
x std::string x{f()};
}
parameters g()

243

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Return Value Optimization


std::string f()
{
std::string a{"A"};
locals b:23
b int b{23};

return a;
parameters ?
&x }
f()
void g()
{
locals x
x:"A" std::string x{f()};
}
parameters g()

24
4

Pass-By-Value Copy Elision


Passing temporaries by value is another copy elision opportunity.
Pass-by-value implies callee can change its copy of the argument without being
observed by caller
§ Caller allocates space for callee’s by-value parameters on stack.
§ Any lvalue arguments get copied into that space (no elision happens)
§ Any rvalue arguments are simply constructed in that space to begin with

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 245

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Unoptimized Argument Passing


locals b:23
b void f( std::string a )
{
int b{23};
parameters a:"A"
a …
return;
f() }

temporaries •:"A"
• void g()
{
f(std::string{"A"});
locals y std::vector<int> y;
}
parameters g()

24
6

Copy Elision: Argument Passing


void f( std::string a )
{
int b{23};
locals b:23
b …
return;
}
parameters •:"A"
a:"A"
a
void g()
f() {
f(std::string{"A"});
locals y std::vector<int> y;
}
parameters g()

24
7

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Local Objects and return


For local objects, applying std::move in return a pessimization!
§ Compiler permitted to elide copy/move of local to return value.
§ Not so for function return values (e.g., from std::move).
Widget f1()
{
Widget w;

return w; // good: move of w may be elided
}
Widget f2()
{
Widget w;

return std::move(w); // bad: move of w may not be elided!
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 248

Local Objects and return


For by-value parameters, the compiler is already require to treat this:
Widget f1(Widget w)
{

return w; // will move to return value if move is possible,
} // otherwise will copy
as if you had written this:
Widget f1(Widget w)
{

return std::move(w); // unnecessary, poor style
}
In this case it isn’t wrong to use std::move on the returned parameter, but it is
unnecessary and may be confusing to the reader.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 249

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Summary of return Rules


How to return identifier id of type T, based on what id is:
§ RRef parameter: return std::move(id);
§ URef Parameter: return std::forward<T>(id);
§ Non-reference: return id;

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 250

Return Types
All examples have shown by-value returns:
Widget f()
{
Widget w;

return w;
}
Matrix operator+(Matrix&& temp, Matrix const& y)
{
temp += y;
return std::move(temp);
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 251

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Reference Return Types


Because returning references typically dangerous!
§ Reference-to-local dangles at birth:
Widget&& f()
{
Widget w;

return std::move(w); // std::move needed to bind w to RRef
} // w destroyed before caller
// receives reference
Æ RRef → LRef changes nothing.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 252

Reference Return Types


§ RRef-based returns can dangle after statement containing call:
Widget&& f(Widget&& w)
{

return std::move(w);
}
Widget makeWidget(); // factory function
Widget const& rw{f(makeWidget())}; // rw dangles
Æ makeWidget returns temporary object.
Æf takes and returns RRef to it.
Æ rw is initialized with it.
Æ Temporary object is destroyed.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 253

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Reference Return Types


§ LRef-to-const returns no better:
Widget const& f(Widget const& w)
{

return w;
}
Widget makeWidget(); // factory function
Widget const& rw{f(makeWidget())}; // rw still dangles
Æ Different types for w don’t help:
Widget const& f(Widget&& w); // same risk as above
Widget const& f(Widget& w); // above call won’t compile
// (See also next slide.)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 254

Reference Return Types


§ Even LRef-to-non-const return is risky:
Widget& f(Widget& w)
{

return w;
}

auto pw(std::make_unique<Widget>()); // create heap obj.


// (std::make_unique
… // is C++14 only)
Widget& rw{f(*pw)}; // make rw refer to it

pw = nullptr; // destroy heap obj.
… // rw dangles!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 255

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guideline
Pass and return rvalue references via std::move, universal references via
std::forward.

Based on EMC++ Item 25.


Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 256

Avoid Overloading on Universal References


Overloading + URefs almost always an error.
§ Makes no sense: URefs handle everything.
Æ Lvalues, rvalues, consts, non-consts, volatiles, non-volatiles, etc.
Æ They’re universal references!
§ Counterintuitive behavior.
Æ As we’ll see.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 257

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

URefs and Overloading


struct MessedUp {
template<class T> // goal: handle lvalues
void doWork(T const& param); // reality: handle const lvalues
template<class T> // goal: handle rvalues
void doWork(T&& param); // reality: handle everything
}; // except const lvalues

MessedUp m;
Widget w;
Widget const cw;
m.doWork(w); // doWork(T&&)
m.doWork(std::move(w)); // doWork(T&&)
m.doWork(cw); // doWork(T const&)
m.doWork(std::move(cw)); // doWork(T&&)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 258

URefs and Construction


A class initializable with a name or an ID mapping to a name:
std::string nameFromID(int ID);
struct Person {
Person(std::string const& n): name{n} {} // from name
Person(int ID): name{nameFromID(ID)} {} // from ID
private:
std::string name;
};

std::string jkr{"J. K. Rowling"};


Person p1{jkr}; // fine
Person p2{"John Grisham"}; // fine, but copies temp
// (an rvalue) into name
Person p3{44245}; // fine
Person p4{nameFromID(44245)}; // fine, but copies rvalue

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 259

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

URefs and Construction


Perfect forwarding constructor is more efficient:
struct Person {
template<class NameT>
Person(NameT&& n) // now takes URef
: name{std::forward<NameT>(n)} {}
Person(int ID): name{nameFromID(ID)} {} // as before
private:
std::string name;
};

std::string jkr{"J. K. Rowling"};


Person p1{jkr}; // fine (same as before)
Person p2{"John Grisham"}; // fine, now initializes
// name from string literal
Person p3{44245}; // fine (same as before)
Person p4{nameFromID(44245)}; // fine, now moves rvalue
// into name
Seems okay, but overloading on URef worrisome.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 260

URefs and Construction


With good reason:
struct Person { // as on previous slide
template<class NameT>
Person(NameT&& n): name{std::forward<NameT>(n)} {}
Person(int ID): name{nameFromID(ID)} {}
private:
std::string name;
};

int idBlock(); // find block holding ID


std::size_t offset; // offset into block for name

Person p1{idBlock() + 22}; // fine
Person p2{idBlock() + offset}; // error! tries to initialize
// name with a number!
§ int + std::size_t unsigned type!
Æ Matches URef exactly, but int only with conversion.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 261

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

URefs and Construction


URef overloading really is the problem:
struct Person {
template<class NameT> // best match for
Person(NameT&& n) // all types except
: name{std::forward<NameT>(n)} {} // int
Person(int ID) // best match for
: name{nameFromID(ID)} {} // int only

};
Function templates taking URefs are greediest functions in C++.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 262

Reining in URefs
Curbing greed possible, but not pretty:
struct Person {
template<class NameT, // disable
class = typename std::enable_if< // ctor
!std::is_integral<NameT>::value // for
>::type> // integral
Person(NameT&& n) // types
: name{std::forward<NameT>(n)} {}
Person(int ID) // as
: name{nameFromID(ID)} {} // before

};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 263

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Special Member Functions


Special member functions may be automatically generated:
§ Constructors: default, copy, move
§ Assignment operators: copy, move
§ Destructor
Highlighted functions (normally) take one parameter.
struct Widget {

Widget(Widget const&); // may be auto-generated
Widget(Widget&&); // may be auto-generated
Widget& operator=(Widget const&); // may be auto-generated
Widget& operator=(Widget&&); // may be auto-generated

};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 264

URefs, Overloading,
and Special Member Functions
Consider class with “universal” copy/moving templates:
struct Widget {

template<class T>
Widget(T&&); // “universal” copy/move ctor
template<class T>
Widget& operator=(T&&); // “universal” copy/move op=

};
Seems to handle all argument types:
§ Lvalues + rvalues
§ const + non-const
§ volatile + non-volatile

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 265

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

URefs, Overloading,
and Special Member Functions
Not that simple.
§ Overloading on URefs may be present.
Templates don’t suppress generation of special member functions.
§ Such functions generated overloading on URefs.
struct Widget {

template<class T>
Widget(T&&); // “universal” copy/move ctor
Widget(Widget const&); // generated copy ctor
Widget(Widget&&); // generated move ctor
template<class T>
Widget& operator=(T&&); // “universal” copy/move op=
Widget& operator=(Widget const&); // generated copy op=
Widget& operator=(Widget&&); // generated move op=

};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 266

URefs, Overloading,
and Special Member Functions
Resulting behavior can again surprise:
struct Widget {

template<class T>
Widget(T&&); // “universal” copy/move ctor
Widget(Widget const&); // generated copy ctor
Widget(Widget&&); // generated move ctor

};

Widget w;
Widget const cw;
Widget copyLvalue{w}; // “universal” copy ctor
Widget copyRvalue{std::move(w)}; // generated move ctor
Widget copyConstLvalue{cw}; // generated copy ctor
Widget copyConstRvalue{std::move(cw)}; // “universal” move ctor
operator= behaves analogously.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 267

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

URefs and Overloading


Story similar for non-member templates:
template<class T> // handles non-volatile
void doWork(T const& param); // const lvalues only
template<class T>
void doWork(T&& param); // handles everything else

Widget w;
Widget const cw;
doWork(w); // doWork(T&&)
doWork(std::move(w)); // doWork(T&&)
doWork(cw); // doWork(T const&)
doWork(std::move(cw)); // doWork(T&&)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 268

Problem Scenario Summary


URef overloading tends to arise when:
§ Lvalues/rvalues need to be distinguished when forwarding.
Æ Implies URef parameter.
§ Some types get special treatment.
Æ Implies non-URef parameter.
See EMC++ Item 26 for ways of dealing with this issue.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 269

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Overloading Guideline
§ Overloading on RRef + LRef: typically OK.
§ Overloading on a URef: typically not OK.
Remember push_back versus emplace_back:
template<class T, class Allocator=allocator<T>>
struct vector {

void push_back(T const& x); // LRef (copy lvalues)
void push_back(T&& x); // RRef (move rvalues)
template<class... Args>
void emplace_back(Args&&... args); // URef (forward
… // everything)
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 270

Guideline
Avoid overloading on universal references.

Based on EMC++ Item 26.


Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 271

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Understand Reference Collapsing


Manually declaring references to references is illegal:
Widget w;

Widget& & rrw{w}; // error!

But refs-to-refs can arise during type deduction and evaluation.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 272

Type Deduction for URefs


Function templates taking URefs employ special type deduction:
template<class T>
void f(T&& param);
§ Lvalue arg to f T an LRef (T&)
§ Rvalue arg to f T just T (a non-reference)
Widget w;
f(w); // T is Widget&
f(std::move(w)); // T is Widget (not Widget&&!)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 273

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Ref-To-Ref Generation
Lvalues thus yield a ref-to-ref:
template<class T> // as before
void f(T&& param);
Widget w;
f(w); // generates
// void f<Widget&>(Widget& && param);

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 274

Reference Collapsing
Generated refs-to-refs undergo reference collapsing:
§ T& & T&
§ T&& & T&
§ T& && T&
§ T&& && T&&
IOW,
§ RRef-to-RRef RRef
§ LRef-to-anything LRef
Æ Stephan T. Lavavej: “Lvalue references are infectious.”

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 275

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

type&& Really Does Mean RRef!


The lie is exposed:
§ URefs are RRefs in ref-collapsing contexts.
Æ Technically, T&& is always an RRef.
Function template parameters most common such context :
template<class T>
void f(T&& param); // acts like a URef, is an RRef
Widget w;
f(w); // f(Widget& &&)
// f(Widget&)
f(std::move(w)); // f(Widget&&)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 276

auto
Another ref-collapsing context.
§ Uses template type deduction rules (plus a bit more).
Widget w;

auto&& v1(w); // lvalue initializer
// auto&& → Widget& &&
// Widget&
auto&& v2(std::move(w)); // rvalue initializer
// auto&& → Widget&&

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 277

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

typedef / Type Alias


Also a ref-collapsing context:
template<class T>
struct Widget {
using LvalueRefType = T&;

};
using // int& &&
RRtoWLRT = Widget<int>::LvalueRefType&&; // int&

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 278

decltype
decltype(expr) yields T& or T, and ref-collapsing applies.
§ Sounds like templates and auto.
§ Isn’t.
Æ Type evaluation rules are different:
w decltype(id) id’s declared type
w decltype(non-id lvalue expr) expr’s type; LRef (T&)
w decltype(non-id rvalue expr) expr’s type; non-ref (T)
Widget w;
decltype(w)&& r1{std::move(w)}; // r1’s type is Widget&&
decltype((w))&& r2{std::move(w)}; // r2’s type is Widget&
// (code won’t compile:
// initializer is rvalue)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 279

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Reference Collapsing vs. URefs


Ref-collapsing explains all, but URef abstraction still useful:
§ Code comprehension.
Æ Avoids type&& RRef error.
§ Communication among developers
Æ Say/write RRef only when it can’t be an LRef.
template<class T>
void f(T&& param); // URef, not RRef
for (auto&& i : factory()) … // URef, not RRef

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 280

Reference Collapsing vs. URefs


Type alias and decltype expressions best based on ref-collapsing analysis:
using GTType = Gadget::TMP::type&&; // GTType could be
// LRef or RRef
SomeType var;

decltype((var))&& v{std::move(var)}; // what is v’s type?
§ decltype((var)) is SomeType&.
§ Ref-collapsing v’s type is SomeType&.
§ Initializer is rvalue, so
Æ SomeType a const type code compiles.
Æ SomeType a non-const type code doesn’t.
Widget const w; // w is const type
decltype((w))&& v1{std::move(w)}; // fine
Gadget g;
decltype((g))&& v2{std::move(g)}; // error! can’t init
// non-const LRef w/rvalue

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 281

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guideline
Understand reference collapsing.

Based on EMC++ Item 28.


Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 283

Familiarize Yourself with


Perfect Forwarding Failure Cases
Perfect forwarding promises that
f( arg1, arg2, … , argn );
and
template<class... T> // pft = “perfect
auto pft(T&&... params) -> // forwarding
decltype(f(std::forward<T>(params)...)) // template”
{

return f(std::forward<T>(params)...);

}
pft( arg1, arg2, … , argn );
call the same f with the same arguments.
§ I.e, that pft forwards its arguments to f perfectly.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 284

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Perfect Forwarding
pft forwards to f whatever it gets.
§ Lvalue passed to pft ⇒ f gets the lvalue.
§ Ditto for rvalue.
§ Ditto for const and volatile.
Problem:
§ Lvalue/rvalue/const/volatile don’t cover everything.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 285

Function Calls vs. Forwarding Calls


Function call ⇒ compiler checks arg types against param types:
arg param
Caller fs
types types

Forwarding function call ⇒ compiler also performs type deduction:


arg type arg param
Caller pft fs
types deduction types types

Problem situations:
§ Argument type is “ambiguous.”
§ Argument type can’t be deduced.
§ Declaration-only argument not forwardable.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 286

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Non-nullptr Nulls
To programmers, sometimes 0 ⇒ null, but to compilers, 0 ⇒ int:
void f(int*); // original function
template<class T> // forwarding template
void pft(T&& param)
{

f(std::forward<T>(param));

}
f(0); // fine
pft(0); // error! can’t pass int as int*

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 287

Non-nullptr Nulls
NULL doesn’t (portably) help:
f(NULL); // fine
pft(NULL); // error!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 288

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

nullptr
nullptr does:
void f(int*); // as before
template<class T> // as before
void pft(T&& param)
{

f(std::forward<T>(param));

}
f(nullptr); // fine
pft(nullptr); // also fine

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 289

Braced Initializers
Template type deduction fails for such initializers.
void f(std::string); // original function
template<class T> // forwarding template
void pft(T&& param)
{

f(std::forward<T>(param));

}

char c;

f({ '*', c, '*' }); // fine
pft({ '*', c, '*' }); // error! can’t deduce type for
// { '*', c, '*' }

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 290

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Braced Initializers
Interestingly, type deduction succeeds for auto:
§ Only difference between auto and template type deduction.
auto x = { '*', c, '*' }; // fine, x’s type is
// std::initializer_list<char>
Affords a workaround for callers:
f({ '*', c, '*' }); // still fine
pft({ '*', c, '*' }); // still an error
auto il = { '*', c, '*' };
pft(il); // fine

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 291

Braced Initializers
Workaround burden can be shifted to the interface.
§ Replace forwarding template w/forwarding functions:
void f(std::string); // as before
void fwd(std::string const& param) // forwarding function
{ // for lvalue strings

f(param);

}
void fwd(std::string&& param) // forwarding function
{ // for rvalue strings

f(std::move(param));

}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 292

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Braced Initializers
Solves the problem:
f({ '*', c, '*' }); // fine
fwd({ '*', c, '*' }); // also fine
But can lead to unnecessary temporaries:
f("xyzzy"); // init f’s param via char const* ctor
fwd("xyzzy"); // bind fwd’s param to temporary
// std::string, then init f’s param
// with that

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 293

Braced Initializers
Function + template not perfect, but pretty good:
void fwd(std::string&& param) // #1: for non-const
{ // rvalue std::strings

f(std::move(param));

}
template<class T> // #2: for everything else
void fwd(T&& param)
{

f(std::forward<T>(param));

}

fwd("xyzzy"); // #2 forwards char const[5]


// to f w/o temp
std::string s{"BTvS"};
fwd(s); // #2 forwards std::string&
// to f w/o temp
fwd({ '*', c, '*' }); // #1 forwards
// std::string&&-to-temp to f
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 294

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Declaration-Only Integral const static Members


Binding the member to a reference ⇒ definition required.
struct Widget {
static std::size_t const size{100}; // declaration (definition required
… // at namespace scope)
};
… // no def’n for Widget::size

void f(std::size_t); // original function


template<class T> // forwarding template
void pft(T&& param)
{

f(std::forward<T>(param));

}
f(Widget::size); // fine
pft(Widget::size); // link-time error!
// Widget::size undefined

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 295

Template Names
Templates lack types ⇒ can’t be deduced.
void f( void(int) ); // original function;
// ≡ “void f( void(*)(int) )”
template<class T> // forwarding template
void pft(T&& param)
{

f(std::forward<T>(param));

}

template<class T>
void funcTempl(T);
f(funcTempl); // fine, instantiates funcTempl<int>
pft(funcTempl); // error! can’t deduce type for funcTempl

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 296

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Template Names
Example is standard stream manipulators:
template <class charT, class traits> // from C++ Standards
basic_ostream<charT,traits>&
flush(basic_ostream<charT,traits>& os);
template<class T>
void print(T&& param)
{

std::cout << std::forward<T>(param);

}
std::cout << std::flush; // fine, same as
// operator<<(std::cout, std::flush);
// calls operator<<( ostream&(ostream&) )
print(std::flush); // error!
§ Ditto for std::endl, std::ends, std::ws

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 297

Non-const Lvalue Bitfields


Such bitfields can’t be bound to references.
struct IPv4Header {
std::uint32_t version:4, // per IPv4 Wikipedia entry
IHL:4,
DSCP:6,
ECN:2,
totalLength:16;

};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 298

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Non-const Lvalue Bitfields


void f(std::size_t); // original function
template<class T> // forwarding template
void pft(T&& param)
{

f(std::forward<T>(param));

}

IPv4Header h;

f(h.totalLength); // fine
pft(h.totalLength); // error!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 299

Summary
Perfect forwarding failure cases:
§ 0 or NULL as null pointer.
§ Braced initializers.
§ Declaration-only integral const static members.
§ Template names.
§ Non-const lvalue bitfields
§ Others?

“Perfect forwarding” not perfect, but still quite good.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 300

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guideline
Familiarize yourself with perfect forwarding failure cases.

Based on EMC++ Item 30.


Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 301

Summary: Rvalue References,


Move Semantics, and Perfect Forwarding
§ Assume that move operations are not present, not cheap, and not used.
§ Distinguish universal references from rvalue references.
§ Pass and return rvalue references via std::move, universal references via
std::forward.
§ Avoid overloading on universal references.
§ Understand reference collapsing.
§ Familiarize yourself with perfect forwarding failure cases.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 302

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

STL
Algorithms
Containers

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 303

Concepts and Architecture of the STL

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 304

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Pointers Into Arrays


In both C and C++, a pointer into an array may point to any element of the
array or to one beyond the last element:

p q

Within this range,


§ Any pointers between p and q (inclusive) may safely be compared.
§ Any pointer except q may safely be dereferenced.
The range between p and q — including where p points but excluding
where q points — is known as a half-open range:
§ It is sometimes written [p, q).
§ The notion of a half-open range is fundamental to the STL.

This overview of the STL is based on MEC++ Item 35.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 305

Searching an Array
Suppose we need a function to find the first occurrence of a value in an
array of ints.
Here’s one approach:
int* find(int *begin, int *end, int value)
{
while (begin != end && *begin != value) ++begin;
return begin;
}
§ We use the half-open range [begin, end) to specify the array to search.
§ We return a pointer to the first element containing value.
§ We detect the end of the range by checking to see if begin!=end.
Æ This generalizes better than checking to see if begin<end.
§ If value isn’t found, we return end.
Æ This generalizes better than NULL.
Æ To clients, end is as distinguishable as NULL, because it clearly points
outside the array.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 306

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Using find
Search an entire array:
int values[50];
...
int *firstFive{find(values, values+50, 5)}; // search values for 5
if (firstFive != values+50) {
... // found it, firstFive points to it
}
else {
... // not found, *firstFive is
} // undefined
Search part of an array:
int n;
std::cin >> n;
int *firstn{find( values+10, // search for n in values[10]
values+20, // through values[19]
n)};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 307

Generalizing find
Templatizing find allows it to work for arrays of any type:
template<class T>
T* find(T *begin, T *end, T const& value)
{
while (begin != end && *begin != value) ++begin;
return begin;
}
The original code still works:
int values[50];
...
int *firstFive{find(values, values+50, 5)}; // still works
find works with other types of arrays, too:
char letters[26];
...
char *firstA{find(letters, letters+26, 'A')}; // also works

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 308

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Generalizing find Again


Look again at find:
template<class T>
T* find(T *begin, T *end, T const& value)
{
while (begin != end && *begin != value) ++begin;
return begin;
}
Consider the operations performed on the T* pointers:
§ Pass-by-value
§ Comparison for inequality
§ Dereferencing
§ Preincrement

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 309

Generalizing find Again


These are all operations we can write ourselves:
§ Pass-by-value: copy constructor
§ Comparison for inequality: operator!=
§ Dereferencing: operator*
§ Preincrement: operator++
find could thus work with any pointer-like object that supports these
operations.
In the STL, pointer-like objects are known as iterators.
§ Iterators can be thought of as restricted generalized pointers:
Æ They’re restricted, because they don’t necessarily support all pointer
operations.
Æ They’re generalized, because they don’t have to be real pointers.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 310

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Generalizing find Again


Here’s find written to work with iterators:
template<class T, class It>
It find(It begin, It end, T const& value)
{
while (begin != end && *begin != value) ++begin;
return begin;
}
The original code still works:
int values[50];
...
int *firstFive{find(values, values+50, 5)}; // still works
The compiler figures out that T is int and It is int*.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 311

std::find
find is part of the STL, but a standard-conforming implementation looks
slightly different:
template<class It, class T> // param order is reversed
It find(It begin, It end, T const& value)
{
while (begin != end &&
!(*begin == value)) // uses == instead of !=
++begin;
return begin;
}
These changes are insignificant to callers.
§ All examples we’ve seen continue to work with this implementation.
§ This implementation requires only that T support operator==, not
necessarily operator!=.
Æ Well-designed types support both if they support either.

From now on, we’ll show only standard-conforming find implementations.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 312

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

STL Algorithms
Function templates like find are known as algorithms.
§ Algorithms use iterators to specify half-open ranges defining the
sequences of values on which the algorithms should operate.

template<class It, class T>


It find(It begin, It end, T const& value);

Algorithm Half-Open Range

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 313

find and Containers


Now that find uses iterators, it is no longer restricted to working with
arrays.
§ As long as a data structure offers iterators that support the four
required operations,
Æ Copy construction
Æ Comparison for equality
Æ Dereferencing
Æ Preincrement
find will work.
One such data structure in the STL is list:
§ It’s a doubly-linked list.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 314

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Using find with list


To call find, we must have a way to identify the beginning and “one past
the end” iterators for a list.
§ A priori, there’s no way to know this, because we don’t know how list
is implemented.
Like all STL containers, list provides member functions that return the
necessary iterators:
§ begin() returns an iterator identifying the beginning of the list.
§ end() returns an iterator identifying “one past the end” of the list.
Modern C++ supports free-standing begin() and end() which take the
container as a parameter. This is more general (is supports C-style arrays),
so we prefer it.
We also need to know the type of list’s iterator, because we need that to
hold the return value from find.
Like all STL containers, list provides a public type alias:
§ iterator is the name of the type of list’s iterator.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 315

Using find with list


Hence:
std::list<int> myList;
...
std::list<int>::iterator firstFive{find(begin(myList), end(myList), 5)};
if (firstFive != end(myList)) {
... // found it, firstFive points to it
}
else {
... // not found, *firstFive is undefined
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 316

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Using find with list


This use of find instantiates the same template as before, but it generates
quite different object code:

next next next next


prev prev prev prev
data data data data

§ operator* for list::iterator has to return one field from a structure.


§ operator++ for list::iterator chases a pointer to move to the next element
of the linked list.
Contrast this with the code generated for dereferencing and incrementing a
built-in pointer, as in our original array example.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 317

find and Other Containers


The STL offers several other container types:
§ string
§ vector: like an extensible array
§ deque: an array-like structure optimized for insertion on both ends
§ set
§ etc.
find works with all of them.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 318

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

find and Other Containers


find and string:
std::string myString;
...
std::string::iterator firstA{find(begin(myString), end(myString), 'A')};
if (firstA != end(myString)) {
... // found it, firstA points to it
}
else {
... // not found, *firstA is undefined
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 319

find and Other Containers


find and vector:
std::vector<int> myVector;
...
std::vector<int>::iterator firstFive{find(begin(myVector), end(myVector), 5)};
if (firstFive != end(myVector)) {
... // found it, firstFive points to it
}
else {
... // not found, *firstFive is undefined
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 320

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

find and Other Containers


find and deque:
std::deque<int> myDeque;
...
std:: deque<int>::iterator firstFive{find(begin(myDeque), end(myDeque), 5)};
if (firstFive != end(myDeque)) {
... // found it, firstFive points to it
}
else {
... // not found, *firstFive is undefined
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 321

find and Other Containers


find and set:
std::set<int> mySet;
...
std::set<int>::iterator firstFive{find(begin(mySet), end(mySet), 5)};
if (firstFive != end(mySet)) {
... // found it, firstFive points to it
}
else {
... // not found, *firstFive is undefined
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 322

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

find and Other Containers


As we’ve seen, find performs a linear-time search.
Some STL containers offer a faster member function, also named find:
§ These include set, multiset, map, multimap, and hash-based containers
unordered_set, unordered_multiset, unordered_map, and
unordered_multimap:
set<int>::iterator firstFive{mySet.find(5)}; // logarithmic-time
// search
if (firstFive != end(mySet)) {
... // found it...
}
else {
... // not found...
}
§ The existence of such member functions doesn’t change the
fundamental point:
Æ find works with every STL-compatible container!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 323

STL and Conventions


The STL is held together by conventions:
§ Containers represent sequences of values.
§ Containers provide iterators that know how to traverse the sequences
they represent.
Æ Clients access iterators via member functions like begin and end and
via type aliases like iterator.
§ Algorithms use iterators to define the sequences of values on which
they should operate.
Æ These sequences are specified via half-open ranges.

This leads to three of the four fundamental components of the STL:


§ Containers
§ Iterators
§ Algorithms
(The fourth is function objects, which we’ll discuss shortly.)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 324

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

The Extensibility of the STL


Because the STL is based on conventions:
§ You can add new containers:
Æ Ifthey follow the conventions, STL-compliant algorithms will work
with them.
§ You can add new algorithms:
Æ Ifthey follow the conventions, STL-compliant containers will work
with them (via the containers’ iterators).
§ You can add new iterators:
Æ Example: an iterator that skips every other element.
Æ If the new iterator types follow the conventions, STL-compliant
algorithms will work with them.
§ You can add new function objects:
Æ If they follow the conventions, everything just works.
There are no required base classes, no virtual functions to redefine, etc.
§ Follow the conventions, and you’re in!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 325

Generalizing find Further


Look again at find:
template<class It, class T>
It find(It begin, It end, T const& value)
{
while (begin != end && !(*begin == value)) ++begin;
return begin;
}
This algorithm hard-wires in the test for the desired value by using ==.
Let’s generalize this test to be a user-defined predicate:
template<class It, class T>
It find_if(It begin, It end,
bool pred(T const&)) // same as bool (*pred)(T const&)
{
while (begin != end &&
!pred(*begin)) ++begin; // same as !((*pred)(*begin))
return begin;
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 326

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Generalizing find Further


We can use find_if like this:
bool equalsFive(int const& value) { return value == 5; }
int values[50];
...
int *firstFive{find_if(std::begin(values), std::end(values), equalsFive)};
if (firstFive != std::end(values)) ...
We can also use it like this:
bool multipleOf5(int const& value) { return value % 5 == 0; }
std::vector<int> values;
...
auto firstMultiple(
find_if( begin(values), // find the first multiple of 5
end(values), // in the vector
multipleOf5));
if (firstMultiple != end(values)) ...

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 327

Generalizing find Further


Look again at find_if:
template<class It, class T>
It find_if(It begin, It end, bool pred(T const&))
{
while (begin != end && !pred(*begin)) ++begin;
return begin;
}
The predicate in this algorithm is unnecessarily constrained:
§ Its return type must be bool.
Æ Why not just convertible to bool, e.g., int, char, pointer, etc.?
§ It must take an argument of type T const&.
Æ Why not anything into which T can be implicitly converted,
including T itself (i.e., pass-by-value)?
§ It must be a pointer to a function.
Æ Why not anything to which a parameter list may be applied, e.g., an
object of a class that has overloaded operator()?

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 328

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Generalizing find Further


This version of find_if lifts all these restrictions:
template<class It, class Pred>
It find_if(It begin, It end, Pred p)
{
while (begin != end && !p(*begin)) ++begin;
return begin;
}
We no longer need to specify T, because T is now part of the type Pred.
This is the version of find_if that’s in the STL.
§ It’s another algorithm.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 329

Using find_if
Our original find_if example continues to work:
bool equalsFive(int const& value) { return value == 5; }
int values[50];
...
int *firstFive{find_if(values, std::end(values), equalsFive)};
if (firstFive != std::end(values)) ...
The compiler figures out that It is int* and Pred is bool (*)(int const&).
§ Notice how the former type T (which was int) is now part of Pred.
Note that the std::begin() for an array is optional. The array name itself
decays into a pointer to the first element, which is what std::begin() returns.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 330

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Using find_if
The vector example continues to work, too:
bool multipleOf5(int const& value) { return value % 5 == 0; }
std::vector<int> values;
...
auto firstMultiple(
find_if( begin(values), // find the first multiple of 5
end(values), // in the vector
multipleOf5));
if (firstMultiple != end(values)) ...
The compiler figures out that It is vector<int>::iterator and Pred is
bool(*)(const int&).

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 331

Using find_if
If we change multipleOf5 to use pass-by-value and to return an int, find_if
still works:
int multipleOf5(int value) { return value % 5 == 0 ? 1 : 0; }
std::vector<int> values;
...
auto firstMultiple(
find_if( begin(values), // find the first multiple of 5
end(values), // in the vector
multipleOf5));
if (firstMultiple != end(values)) ...
The compiler figures out that It is vector<int>::iterator and Pred is int (*)(int).
Inside find_if, multipleOf5’s int return type is implicitly converted to bool in
this statement:
while (begin != end && !p(*begin)) ... // p’s return type is implicitly
// converted to bool

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 332

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Function Objects
We can also use a function object as a predicate for find_if.
§ A function object is an object instantiated from a class declaring
operator() — the function call operator.
§ Function objects are also known as functors.
Æ Note: This a strictly C++ usage of the term. Functor has a completely
different meaning in the world of Functional Programming.
A class for checking if a value is within an epsilon of another value:
struct WithinEpsilon {
WithinEpsilon(double value, double epsilon)
: val{value}, ep{epsilon} {}
bool operator()(double x) const { return fabs(x-val) <= ep; }
private:
double val;
double ep;
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 333

Function Objects
It can be used with find_if like this:
WithinEpsilon checkVal{5.0, 0.3}; // checkVal returns true when it’s
// passed a value within 4.7-5.3
std::vector<float> values;
...
auto firstWithinEpsilon(
find_if( begin(values), // find the first element in values
end(values), // satisfying checkVal
checkVal));
if (firstWithinEpsilon != end(values)) ...
Here, the compiler figures out that It is vector<float>::iterator and Pred is
WithinEpsilon.
Consider this expression inside find_if:
while (begin != end && !p(*begin)) ... // 1. Convert *begin (which is
// float) to double
// 2. Invoke p.operator(),
// passing the double

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 334

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Function Objects
STL programmers rarely declare standalone function objects. Instead, they
create temporary function objects when they are needed:
std::vector<float> values;
...
auto firstWithinEpsilon( // again, find the first element
find_if( begin(values), // in the range 4.7-5.3
end(values),
WithinEpsilon{5.0, 0.3})); // create temporary
// WithinEpsilon object to
// pass to find_if
if (firstWithinEpsilon != end(values)) ...

This is very often done with lambda


functions. We’ll cover lambda
functions in depth in the next section.
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 335

Function Objects
Function object classes are nice, because they can generate lots of different
function objects:
...
auto pElem1( // find the first element
find_if( begin(values), // in the range -1.2 to 4.2
end(values),
WithinEpsilon{1.5, 2.7})); // create temporary
...
auto pElem2( // find the first element
find_if( begin(values), // in the range -0.1 to 0.1
end(values),
WithinEpsilon{0.0, 0.1})); // create temporary

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 336

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Function Objects
Templates can generate multiple functions, too, but each template
instantiation is a different function:
§ That can lead to code bloat (for non-inline functions).
Æ Only one copy of WithinEpsilon’s member functions will be
generated, no matter how many objects are created.
Function objects are also more flexible than templates:
§ Their behavior can be customized via constructor arguments and other
member function calls.
§ “Calls” (i.e., invocations of operator()) can be inlined.
Æ This is rarely done when function pointers are passed as parameters.
Lambda expression combine the performance (and inline-ability) of
function objects with a syntactic expressiveness not available in Classic
C++.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 337

What is “The STL”?


Possible answers:
§ The portion of the standard C++ library concerning containers,
algorithms, and related functionality.
§ The above plus strings.
§ The above plus iostreams.
§ The above plus parts of TR1.
§ The above plus some vendor-specific extensions.
Æ E.g., from Boost, STLport, SGI, and Dinkumware.
§ The entire standard C++ library.
Æ This is just wrong.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 338

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

What is “The STL”?


My informal definition:
§ Anything in the standard C++ library supporting iterators.
Æ Containers, strings, algorithms, and some aspects of iostreams.
w Containers, strings, and iostreams provide iterators.
w Algorithms use them.
C++11 (via TR1) adds to “the STL:”
§ Hash containers and fixed-size arrays are new containers.
§ Regular expressions support iteration over matches.
Modern C++ adds to “the STL:”
§ More containers and algorithms

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 339

STL Architecture Summary


§ There are four fundamental components in the STL.
Æ Containers, Iterators, Algorithms, and Function Objects.
§ Iterators are pointer-like objects.
Æ Pairsare often used to specify half-open ranges on which algorithms
should work.
Æ The “one past the end” iterator is often used to signal failure.

§ Algorithms are function templates.


Æ They often encapsulate loops, e.g., find and find_if.
§ Function objects (aka functors) are function-like objects.
Æ They’re generated by classes that overload operator().
§ The STL is held together by conventions.
Æ This allows for great flexibility and extensibility.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 340

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Overview of Standard Containers


There over two dozen standard containers in the STL, divided into two
groups sequence containers and associative containers.
Sequence containers allow you to store their elements in arbitrary order.
§ vector: essentially an extensible array.
§ string: may hold arbitrary character types.
§ deque: an array-like structure optimized for insertion at both ends.
§ list: doubly-linked.
§ array: fixed-sized, no heap allocation (from Boost via TR1)
Æ Unlike true sequence containers, it offers no insert and erase.
§ forward_list: singly-linked (as of C++11)
Æ Goal: zero time/space overhead compared to hand-written C.
Æ STL container conventions sacrificed to achieve goal

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 341

Overview of Standard Containers


Associative containers provide better-than-linear lookups.
§ set: a sorted collection without duplicates.
§ multiset: a sorted collection allowing duplicates.
§ map: a sorted collection of (key, value) pairs. All keys are unique.
§ multimap: like a map, except keys need not be unique.
All the above associative containers are sorted.
§ unordered_set: a hashed collection without duplicates.
§ unordered_multiset: a hashed collection allowing duplicates.
§ unordered_map: a hashed collection of (key, value) pairs. All keys are
unique.
§ unordered_multimap: like a unordered_map, except keys need not be
unique.
The unordered containers were part of the original STL proposal (under the
name hash_*, but were not accepted until TR1 and C++11.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 342

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Conceptual Container Implementations


vector
string
array

deque

list

set
multiset
map
multimap

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
Slide 343
http://www.aristeia.com/ http://cpp.training/

Conceptual Container Implementations

unordered_set
unordered_multiset
unordered_map
unordered_multimap

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 344

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Coda: The Behavior of remove


remove is the most confusing word in the STL.
§ When you apply remove to the elements of a container, the size of the
container does not change.
Æ Not even if many — or all — of the elements are removed!
Widget w1, w2;
...
std::vector<Widget> vw;
for (int i{0}; i < 5; ++i) { // insert alternating copies of
vw.push_back(w1); // w1 and w2 to vw
vw.push_back(w2);
}
std::cout << vw.size(); // prints 10
std::remove(begin(vw), end(vw), w1); // remove all elements
// equal to w1
std::cout << vw.size(); // still prints 10!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 345

Understand the Behavior of remove


This behavior seems absurd, but there is a good reason for it.
§ remove can’t actually remove anything from a container!
To truly remove something, you must call a container member function,
usually erase.
§ remove has only iterators to work with.
Æ It has no way of knowing to which container they belong.
w The container may not be resizable, e.g., an array or an std::array.
§ Thus, it is impossible for it to make container member function calls.
§ It therefore can’t actually remove anything from a container.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 346

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Understand the Behavior of remove


Because remove can’t really get rid of anything, it does this instead:
§ Shifts “unremoved” elements towards the beginning of the sequence,
overwriting “removed” elements as it goes.
§ Returns an iterator indicating the new end of the sequence, i.e., the
sequence from which the undesired values have been “removed.”
std::vector<int> v; // create a vector and fill it with
... // some values

5 22 9 10 16 10 16 18 16
begin(v) end(v)

auto newEnd( // "remove" all elements with


std::remove(begin(v), end(v), 16)); // value 16

5 22 9 10 16 10 16 18 16

5 22 9 10 10 18 16 18 16
begin(v) newEnd end(v)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 347

Understand the Behavior of remove


5 22 9 10 10 18 16 18 16
v.begin() newEnd v.end()

std::ostream_iterator<int> stdOut{std::cout, " ”};


std::copy(begin(v), newEnd, stdOut); // prints 5 22 9 10 10 18
std::copy(begin(v), end(v), stdOut); // prints
// 5 22 9 10 10 18 16 18 16
std::cout << v.size(); // prints 9

Because remove overwrites element values, it can’t be applied to associative


containers.
§ To remove things from such containers, you must use the erase member
function.
Æ You must have access to an associative container to remove things
from it.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 348

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Understand the Behavior of remove


Conventionally, a call to remove is followed by a call to erase.
§ This changes the size of the container.
v.erase(newEnd, end(v)); // erases elements from newEnd on

5 22 9 10 10 18
begin(v) newEnd v.end(v)

std::cout << v.size(); // prints 6


It is idiomatic to combine these two calls — the erase-remove idiom:
v.erase(std::remove(begin(v), end(v), 16), end(v));
§ The (poorly-named) list::remove achieves the same effect:
std::list<int> li;
...
li.remove(16); // really removes all elements with
// value 16; li.size() can change
Æ list::remove is more efficient than the erase-remove idiom, because it
just splices out list nodes instead of copying values.
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 349

Understand the Behavior of remove


Two other algorithms act like remove:
§ remove_if is just like remove, but it takes a predicate.
§ unique “removes” adjacent duplicate values.
You must follow calls to remove/remove_if/unique with calls to erase if
you really want to get rid of something.
§ Unless you are calling the list member functions.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 350

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guideline
Understand the behavior of remove.

Based on ESTL Item 32.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 351

Algorithms

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 352

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Know Your Sorting Options


Everybody knows about sort, but sometimes that’s overkill.
Suppose you need the 20 best Widgets in a container:
§ You don’t need a full sort.
§ You need only a partial_sort:
struct Widget { ... };
bool qualityCompare(Widget const& lhs, Widget const& rhs)
{
// return whether lhs’s quality is better than rhs’s quality
}
vector<Widget> widgets;
...
std::partial_sort(begin(widgets), // put the best 20 elements
begin(widgets) + 20, // (in order) at the front of
end(widgets), // widgets
qualityCompare);
...

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 353

Know Your Sorting Options


If you need the top 20 Widgets, but you don’t care about their order, even
partial_sort is too much work.
§ nth_element puts a single element in the correct place, with all other
elements correctly preceding or following that element.
std::nth_element( begin(widgets), // put the best 20 elements
begin(widgets) + 19, // at the front of widgets,
end(widgets), // but don’t worry about
qualityCompare); // their order

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 354

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

nth_element
nth_element is remarkably useful:
§ Find the Widget with the median level of quality:
std::vector<Widget>::iterator begin(begin(widgets)); // convenience vars
std::vector<Widget>::iterator end(end(widgets)); // for widgets’ begin
// and end iterators
std::vector<Widget>::iterator goalPosition; // where the widget
// of interest is
goalPosition = begin + widgets.size() / 2; // the widget of interest
// would be in the middle
// of the sorted vector
std::nth_element( begin, goalPosition, end, // find the median quality
qualityCompare); // value in widgets
... // goalPosition now points
// to the Widget with a
// median level of quality

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 355

nth_element
§ Find the Widget with a level of quality at the 75th percentile:
std::vector<Widget>::size_type goalOffset{ // figure out how far
0.25 * widgets.size()}; // from the beginning
// the Widget of
// interest is
std::nth_element( begin, begin + goalOffset, end, // find quality value at
qualityCompare); // the 75th percentile
... // begin+goalOffset
// now points to the
// Widget at the 75th
// percentile level of
// quality

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 356

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

nth_element + sort vs. partial_sort


nth_element can also be used to find the endpoint for “top n” sort.
§ nth_element + sort can be much faster than partial_sort.
Æ Thomas Guest’s results for finding the top 10% of N 31-bit integers.

§ General approach (for top 10% using default comparison function):


container::iterator begin{container.begin()};
container::size_type offset{0.10 * container.size()};
std::nth_element(begin, begin + offset, container.end());
std::sort(begin, begin + offset);

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 357

Know Your Sorting Options


Suppose you want to find all the Widgets with a quality of 2 or better.
§ sort and partial_sort do more work than you need.
§ nth_element doesn’t really do what you want.
§ partition does:
bool hasAcceptableQuality(Widget const& w)
{
// return whether w has a quality rating of 2 or better;
}
std::vector<Widget>::iterator goodEnd{ // move all widgets satisfying
std::partition(begin(widgets), // hasAcceptableQuality to
end(widgets), // the front of widgets, and
hasAcceptableQuality)}; // return an iterator to the first
// widget that isn’t satisfactory
The desired Widgets are now between widgets.begin() and goodEnd.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 358

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Stability
Suppose many Widgets have equivalent quality ratings. How do sort,
partial_sort, nth_element, and partition order them?
§ Any way they want to.
§ You can’t change this.
But sometimes you want stability:
§ A sort is stable if the relative positions of equivalent elements are
guaranteed to be preserved during the sort.
sort isn’t stable, but stable_sort is.
partition isn’t stable, but stable_partition is.
partial_sort and nth_element aren’t stable.
§ The STL offers no stable versions of these algorithms.
§ If you need stability, you probably want to use stable_sort.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 359

Iterator Requirements
sort, stable_sort, partial_sort, nth_element require random access iterators:
§ Applicable to vectors, strings, deques, valarrays, and arrays.
list has a sort member function:
§ It performs a stable sort.
§ To apply the other sorting algorithms to a list, consider these options:
Æ Copy/move list data into a container with random access iterators,
sort that, copy/move the data back.
Æ Create a container of list::iterators, sort that, and access list elements
via the iterators.
Æ Create a container of list::iterators, sort that, then use it to iteratively
splice list elements into the desired order.
partition and stable_partition require only bidirectional iterators.
§ They may be applied to any standard sequence container.
It makes no sense to try to sort the standard associative containers.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 360

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Summary of Sorting Options


For sequence containers other than list:
§ For a full sort, use sort or stable_sort.
§ To order the top n elements, use partial_sort or nth_element + sort.
§ To find the element at position n or find the top n elements (ignoring
order), use nth_element.
§ To separate elements into those that do and don't satisfy a criterion,
use partition or stable_partition.
For list:
§ Use list::sort in place of sort and stable_sort.
§ Use partition and stable_partition directly.
In addition:
§ Standard (non-hashed) associative containers keep things sorted all
the time.
§ So does priority_queue.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 361

Relative Efficiency of the Sorting Algorithms


In general,
§ More work takes longer.
§ Stability costs.
Thus, from fewest resource demands to most:
1. partition
2. stable_partition
3. nth_element
4. partial_sort
5. sort
6. stable_sort

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 362

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Sorting Under the Hood


The Standard doesn’t dictate algorithm implementations, but it’s a good
guess that:
§ sort is implemented via introsort.
Æ Introsort usually uses quicksort, but on pathological inputs, it
switches to heapsort to maintain n lg n worst case performance.
§ partial_sort is implemented via heapsort.
Æ It’s usually slower than sort (for a full sort).
§ stable_sort is implemented via mergesort.
Æ Also slower than sort (for a full sort).
§ nth_element is implemented via a modified quicksort (possibly
introselect).
Æ It runs in linear time on average.

§ list::sort is implemented via mergesort.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 363

Guideline
Know your sorting options.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 364

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Note Which Algorithms Expect Sorted Ranges


Not all algorithms are guaranteed to accept all ranges:
§ sort and nth_element generally demand random access iterators.
§ replace and rotate demand mutable iterators.
If you violate these kinds of preconditions, your code usually won’t
compile.
Some preconditions aren’t checked during compilation.
§ The most common is that a range contain sorted values.
Æ If you pass these algorithms ranges that are unsorted, results are
undefined.
In addition, a few algorithms work best with sorted ranges.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 365

Note Which Algorithms Expect Sorted Ranges


Here are the algorithms that demand sorted ranges:
§ Algorithms that work via binary search:
Æ Binary searches don’t work on unsorted ranges.
binary_search lower_bound
upper_bound equal_range
Æ These run in logarithmic time only if passed random access iterators.
With less powerful iterators, they run in linear time!
Æ The standard says that they are logarithmic in terms of comparisons

§ Algorithms that perform set-theoretic operations in linear time:


Æ If unsorted ranges were accepted, they’d require more time:
set_union set_intersection
set_difference set_symmetric_difference
includes
Æ These algorithms have been generalized to run on multisets, so
ranges may contain duplicate values.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 366

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Note Which Algorithms Expect Sorted Ranges


§ Algorithms that merge sorted ranges in linear time:
Æ On unsorted ranges, they’d also require more time:
merge inplace_merge
All these algorithms use equivalence as their “sameness” test.

The following algorithms work on unsorted ranges, but they’re most useful
when applied to sorted ranges:
unique unique_copy
§ They eliminate adjacent duplicates, so when ranges are sorted, they
eliminate all duplicates:
Æ They “eliminate” elements just like remove does.

§ By default, they use equality as their “sameness” test.


Æ You can override the default with a custom comparison function.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 367

Note Which Algorithms Expect Sorted Ranges


For algorithms depending on sorted ranges, mere sortedness is not enough.
§ The ranges must be sorted the way the algorithms expect them to be.
Æ There are many different ways to sort things!
Here’s an example of what not to do:
std::vector<int> v; // create and populate vector,
... // sort it in descending order
std::sort( begin(v), end(v),
std::greater<int>{});
...
bool a5Exists{ // ouch! search for 5 assuming
std::binary_search(begin(v), // the vector is sorted in
end(v), // ascending order!
5)};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 368

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Note Which Algorithms Expect Sorted Ranges


For ranges not sorted by “<“, you must tell the algorithm what the sorting
criterion is:
bool a5Exists{ // fine, now
std::binary_search(begin(v), end(v), 5, // binary_search
std::greater<int>{})}; // will work
Always be sure the sorting criterion used for the range is consistent with
the criterion used by the algorithm.
§ Usually, this means using the same comparison function.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 369

Guideline
Note which algorithms expect sorted ranges.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 370

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Algorithms Summary
§ Know your sorting options.
§ Note which algorithms expect sorted ranges.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 371

Containers

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 372

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Choose Your Containers With Care


C++ offers a wide variety of containers:
§ Standard STL sequence containers: vector, string, deque, list,
forward_list.
§ Standard STL associative containers: set, multiset, map, multimap,
unordered_set, unordered_multiset, unordered_map,
unordered_multimap.
§ Nonstandard sequence containers: slist and rope.
§ vector<char> as a replacement for string.
§ Sorted vectors as a replacement for the standard associative containers.
§ Standard non-STL containers: arrays, array, bitset, valarray, stack,
queue, priority_queue.
§ Nonstandard tree: btree.
That’s a lot of options. You should consider all of them.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 373

Choose Your Containers With Care


Much advice about choosing containers echos the C++ Standard:
vector, list, and deque offer different complexity trade-offs. vector is
the type of sequence that should be used by default. list should be
used when there are frequent insertions and deletions from the
middle of the sequence. deque is the data structure of choice when
most insertions and deletions take place at the beginning or at the
end of the sequence.
This is decent guidance — if you care only about algorithmic complexity.
§ There’s a lot more to care about.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 374

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Choose Your Containers With Care


Important terms:
§ Contiguous-memory containers may store their elements in one or more
(dynamically allocated) chunks of memory:
Æ Insertions/erasures require moving existing elements.
Æ array, vector, string, deque are contiguous-memory containers.

array

vector
string

deque

Æ So is rope.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 375

Choose Your Containers With Care


§ Node-based containers store one element per chunk of (dynamically
allocated) memory.
Æ Insertions/erasures affects only pointers to nodes; element values
need not be moved
Æ list and the standard tree-based associative containers are node-
based:
list
set
multiset
map
multimap

Æ So are forward_list and the hashed containers.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 376

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Choose Your Containers With Care


Questions to consider for STL and STLesque containers:
§ I generally ignore non-STL containers in what follows.

1. Do you need to be able to insert a new element at an arbitrary position in the


container?
Æ Sequence containers support this, associative containers don’t.

2. Do you care how elements are ordered in the container?


Æ If so, unordered containers are out.

3. Must the container be part of standard C++?


Æ If so, forget rope.

4. What category of iterators do you require?


Æ Random access ⇒ array, vector, deque, and string, but you’d
probably want to consider rope, too.
Æ Bidirectional ⇒ avoid forward_list and the unordered containers.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 377

Choose Your Containers With Care


5. Is it important to avoid movement of existing container elements when
insertions or erasures take place?
Æ If so, stay away from contiguous-memory containers.

6. Does the data in the container need to be layout-compatible with C?


Æ If so, you want vector or std::array. For read-only data, string is
okay, too.
7. Is lookup speed a critical consideration?
Æ If so, look at unordered containers, sorted vectors, and standard
tree-based associative containers — usually in that order.
8. Do you mind if the underlying container uses reference counting?
Æ If so, steer clear of rope.

9. Do you need transactional semantics for insertions and erasures?


Æ If so, use a node-based container.
Æ To get such semantics for multiple-element insertions, choose list.
Æ Transactional semantics are particularly important for writing
exception-safe code.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 378

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Choose Your Containers With Care


10. Do you need to minimize iterator, pointer, and reference invalidation?
Æ If so, use node-based containers.
Æ Insertions/erasures on contiguous-memory containers generally
invalidate everything.
11. Would it be helpful to have a sequence container with random access iterators
where pointers and references to the data are not invalidated as long as nothing
is erased and insertions take place only at the ends of the container?
Æ A very special case, but deque’s case.
Æ Only deque and unordered containers can invalidate iterators
without also invalidating pointers and references.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 379

Choose Your Containers With Care


This list isn’t comprehensive:
§ It fails to consider memory allocation/deallocation strategies of the
various containers.
It should be enough to convince you that there’s more to container
selection than algorithmic complexity.
A “default container”? I don’t think so.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 380

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guideline
Choose your containers with care.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 381

vector and string

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 382

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Prefer vector and string to


Dynamically Allocated Arrays
What’s wrong with using new?
§ Somebody has to remember to use delete.
§ They have to use the correct form of delete:
Æ delete for single objects.
Æ delete [] for arrays.
§ Care must be taken not to call delete more than once.
Æ Deleting memory more than once yields undefined results.
Why put up with the hassles if you don’t have to?
§ vector<T> can almost always replace new T[n] (when T isn’t char)
Æ vector<bool> can be a bit problematic. Consider vector<char>,
deque<bool>, and bitset instead.
§ When vector<T> is too much overhead, consider std::array.
§ string can almost always replace new char[n]
vector and string manage their own memory, so there is no delete problem.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 383

Prefer vector and string to


Dynamically Allocated Arrays
There are other advantages to vector and string:
§ They work seamlessly with STL algorithms.
Æ Algorithms can work on arrays, too, but arrays don’t offer size() or
type aliases like value_type, etc.
Æ std::array does, however.

§ string offers dozens of special-purpose member functions:


Æ E.g., substr, find_first_not_of, replace, etc.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 384

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Prefer vector and string to


Dynamically Allocated Arrays
What are the drawbacks?
§ Communication with legacy APIs?
Æ Not a problem, as we’ll see later. vectors and strings are almost as
convenient as arrays for communicating with C APIs.
§ Use in a multithreaded environment?
Æ This can be a problem for some applications with some non-standard
string implementations.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 385

Guideline
Prefer vector and string to dynamically allocated arrays.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 387

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Use reserve to Minimize Memory Reallocations


for vector and string
vectors and strings automatically grow to accommodate new insertions:
std::vector<int> v;
for (auto i{1}; i <= 1000; ++i) // v automatically grows to
v.push_back(i); // make room for the insertions

std::string alphabet{"abcdefghijklmnopqrstuvwxyz”};
std::string s;
for (auto j{1}; j <=1000; ++j)
s += alphabet; // ditto for s
This is very convenient.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 388

Use reserve to Minimize Memory Reallocations


for vector and string
The convenience is not free.
§ Multiple realloc-like operations typically take place in the previous
examples:
Æ On the platforms I’ve tested, v was reallocated 2-18 times, and s was
reallocated 9-999 times!
Æ Each involves memory allocation (via the container’s allocator)
Æ Each often involves
w Copying container elements from the old memory to the new
memory and
w Destructing the elements in the old memory
§ Each realloc-like operation invalidates all pointers, references, and
iterators into the container:
Æ Other data structures dependent on such pointers/references/
iterators may have to take the time to update themselves

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 389

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Use reserve to Minimize Memory Reallocations


for vector and string
If you know or can estimate the final or maximum size of a vector or string
in advance, you can reduce or avoid reallocations by calling reserve:
std::vector<int> v;
v.reserve(1000);
for (int i{1}; i <= 1000; ++i) // v’s capacity never changes;
v.push_back(i); // we know it’s big enough
std::string alphabet{"abcdefghijklmnopqrstuvwxyz”};
std::string s;
s.reserve(26000);
for (int j{1}; j <=1000; ++j)
s += alphabet; // ditto for s
Benefit:
§ No time spent reallocating memory or copying/destroying elements

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 390

Excess Capacity
If you know only the maximum container size and reserve that much space,
you may end up with excess capacity.
std::vector<int> v;
v.reserve(maxDataValues); // reserve maximum needed space
... // put data into v. When done, v.size()
// may be much less than v.capacity()
std::string s;
s.reserve(maxNumChars); // reserve maximum needed space
... // again, s.size() could be much less
// than s.capacity()

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 391

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

shrink_to_fit()
C++11 introduces a shrink_to_fit() member function for vector, string, and
deque.
To understand what it (probably) does, let’s review ”the swap trick” from
Classic C++:

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 392

The swap Trick


Use “the swap trick” to eliminate the excess capacity:
std::string{begin(s), end(s)}.swap(s); // copy s’s contents into
// an unnamed temporary
// string, then swap their
// internal pointers
std::vector<std::string>{ // move v’s contents into
std::make_move_iterator(begin(v)), // an unnamed temporary
std::make_move_iterator(end(v))}.swap(v); // vector, then swap their
// internal pointers
It works like this:
v or s

temp

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 393

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

The swap Trick


The trick isn’t guaranteed to work perfectly:
§ Implementations are permitted to establish minimum capacities.
Being a trick, it’s best to hide it behind a nice interface:
template<class T>
void trimExcessCapacity(T& container)
{
T(container.begin(), container.end()).swap(container);
}
std::vector<int> v;
std::string s;

trimExcessCapacity(v);
trimExcessCapacity(s);

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 394

Guidelines
Use reserve to minimize memory reallocations for vector and string.
Use shrink_to_fit() (or “the swap trick” in Classic C++) to perform “shrink
to fit” on vectors and strings.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 395

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Know How to Exchange vector and string Data


with Legacy APIs
Problem:
§ C traffics in arrays and char* pointers.
§ In C++, vectors and strings are often preferable.
§ How can we use vectors and strings, yet still talk to C?
There are two cases:
§ Read-only: A C API needs to read vector/string data, but not write it.
§ Read-Write: A C API may need to write vector/string data.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 396

Passing Read-Only vector Data to a C API


Simple: for a vector v, &v[0] is a pointer into v’s data:
void doSomething(int const * pInts, // function in a C API
size_t numInts);
std::vector<int> v;
...
doSomething(v.data(), v.size()); // pass doSomething a pointer
// to v’s memory
Note:
§ std::vector::data() is new in C++11 and comes in const and non-const
Æ Classic C++ solution was &v[0] but this was only defined for size() > 0
§ Do not use begin(v) in place of std::vector::data()!
Æ begin(v) yields an iterator, not a pointer!
Æ Even if the code compiles, it’s wrong.
Æ STL platforms increasingly eschew pointers as vector/string iterators,
so avoiding this error is more important than ever.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 397

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Passing Read-Only string Data to a C API


vector’s approach is also appropriate for string, but only in Modern C++.
This approach was inappropriate for Classic C++ string because:
§ string data may not be stored in contiguous memory.
§ string data may not end with a trailing null.
§ (In practice, both constraints above are were always satisfied.)
The Classic solution is still valid (of course), which is to use string’s c_str
member function instead:
void doSomething(char const*pString); // function in a C API
std::string s;
...
doSomething(s.c_str()); // pass doSomething a
// const char*
// representation of s
Note:
§ std::string::data() in C++11 is guaranteed to be null terminated.
Æ c_str() is always const. A non-const version of data() is in C++17.
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 398

Passing Read-Write vector Data to a C API


A C API may modify the values of elements in a vector v. but:
§ It must not try to change the number of elements in v
Æ e.g., by “adding” elements in v’s unused capacity (if there is any).
§ Failure to heed this restriction will almost always lead to disaster:
Æv will be internally inconsistent (e.g., v.size() will be wrong).
Æ Memory beyond v’s buffer may be corrupted.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 399

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Passing Read-Write vector Data to a C API


Here’s how a C API could write data to a vector:
// C API: write up to arraySize doubles to pArray; return number written.
size_t fillArray(double *pArray, size_t arraySize);

std::vector<double> vd(maxNumDoubles); // create vector of maximum


// size needed
vd.resize(fillArray(vd.data(), vd.size())); // have fillArray write data
// into vd, then resize vd
// to the number of
// elements fillArray wrote
Of course, constraints on the data in the vector must be obeyed.
§ E.g., some vectors hold only sorted data.
§ The C API must enforce such constraints or action must be taken after
the API call to reestablish the constraints.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 400

Passing Read-Write string Data to a C API


In Modern C++, the data in a string is guaranteed to be contiguous and
both the string and a call to data() will be null terminated.
These guarantees were not part of Classic C++, so the safe, portable way to
pass read-write string data to a C API in Classic C++ involved copying
into/out of a vector<char>.
Classic C++ allowed for the COW implementation used by the GCC
standard library. Such an implementation can’t support modification the
string returned by c_str() or data().
Note: As of C++17, data() returns a non-const pointer for non-const strings.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 401

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Passing Data From a C API to Any Container


This approach generalizes:
§ To move data from a C API to an STL container, use a vector as an
intermediary:
std::size_t fillArray( double *pArray,
std::size_t arraySize); // C API
std::vector<double> vd(maxNumDoubles); // intermediary
vd.resize(fillArray(vd.data(), vd.size())); // get data from API
std::deque<double> d{begin(vd), end(vd)}; // copy data into
// deque
std::list<double> l{begin(vd), end(vd)}; // copy data into list
std::set<double> s{begin(vd), end(vd)}; // copy data into set

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 402

Passing Data to a C API from Any Container


This approach can also be used to go the other way:
void doSomething(int const* pInts,
std::size_t numInts); // C API
std::set<int> intSet; // set that will hold
... // data to pass to API
std::vector<int> v{begin(intSet), end(intSet)}; // copy set data into
// vector
doSomething(v.data(), v.size()); // pass the data to
// the API

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 403

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guideline
Know how to exchange vector and string data with legacy APIs.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 404

vector and string Summary


§ Prefer vector and string to dynamically allocated arrays.
§ Use reserve to minimize memory reallocations for vector and string.
§ Call shrink_to_fit() on vector, string and dequeue to trade time for
memory.
§ Know how to exchange vector and string data with legacy APIs.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 405

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Associative Containers

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 406

Always have Comparison Functions Return


false for Equal Values
Suppose you create a set with <= as its sorting criterion:
std::set<int, std::less_equal<int> > s; // s is sorted by “<=”
s.insert(10); // insert the value 10
s.insert(10); // try inserting it again
To understand what happens in the second insert call, let’s distinguish the
two 10s:
§ We’ll call the first one 10A, the second one 10B.
Inside the second call to insert, s must determine whether 10B is already
present.
§ It must thus compare it to 10A.
§ It performs an equivalence test using <= as the comparison function.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 407

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Always have Comparison Functions Return


false for Equal Values
Here’s the test:
!(10A <= 10B) && !(10B <= 10A) // are 10A and 10B equivalent?
Clearly, 10 <= 10, so we have:
!(true) && !(true)
That simplifies to:
false && false ⇒ false
10A is thus not equivalent to 10B, and 10B is inserted.
§ s now has two copies of 10!
Æ It’s a corrupted data structure.
§ The insertion yields undefined behavior, but consider:
Æ How many elements are in s.equal_range(10)?
w Hint: none.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 408

Always have Comparison Functions Return


false for Equal Values
This kind of problem arises for any comparison function that returns true
for equal values.
§ less_equal is just an example.
Suppose you have a less-like comparison function for containers of
pointers to be sorted by the pointed-to values:
struct StringPtrLess {
bool operator()(std::string const*ps1, std::string const*ps2) const
{ return *ps1 < *ps2; }
};
std::set<std::string*, StringPtrLess> ssp; // ssp is sorted by string
// values, not pointer values

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 409

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Always have Comparison Functions Return


false for Equal Values
To get the corresponding greater-like function object class, you might try
this:
struct StringPtrGreater {
bool operator()(std::string const*ps1, std::string const*ps2) const
{ return !(*ps1 < *ps2); } // just negate the old test;
// this is incorrect!
};
That would be bad. The negation of < isn’t >, it’s >=!
§ Equal values will thus test true, and that’s always wrong.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 410

Always have Comparison Functions Return


false for Equal Values
This is the correct function object class:
struct StringPtrGreater {
bool operator()(std::string const*ps1, std::string const*ps2) const
{
return *ps2 < *ps1; // return whether *ps2
} // precedes *ps1 (i.e., swap
// the order of the
}; // operands)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 411

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Always have Comparison Functions Return


false for Equal Values
This Item’s advice applies even when containers allow duplicates:
std::multiset<int, std::less_equal<int> > s; // s is still sorted by “<=”
s.insert(10); // insert 10A
s.insert(10); // insert 10B
auto i(s.find(10));
if (i != s.end()) {
... // we can’t get here
} else {
... // we’ll always get here
}
In this container, 10 isn’t equivalent to 10, so we can never find an element
with that value!
§ Ditto for erase, count, lower_bound, upper_bound, and equal_range.
§ 10 isn’t special. This is true for all values.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 412

Guideline
Always have comparison functions return false for equal values.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 413

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Be Wary of In-Place Key Modification


Motivation:
§ The standard tree-based associative containers are kept in sorted order
by key.
Æ For sets and multisets, the object values are the keys.
Æ For maps and multimaps, the first part component of each stored pair
is the key.
Æ If you could change key values, you might break the sortedness of
the containers.
§ The hash-based containers are also ordered by key:
Æ Again, objects values are keys for hashed sets and multisets, the first
components of pairs are keys for hashed maps and multimaps.
Æ Keys determine the hash bucket each object falls into.
Æ If you could change key values, you might end up with objects in the
wrong buckets.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 414

Be Wary of In-Place Key Modification


Consider why you might want to modify an associative container element:
struct Employee {
...
std::string const& name() const; // get employee name
void setName(std::string const& name); // set employee name
std::string const & title() const; // get employee title
void setTitle(std::string const & title); // set employee title
int idNumber() const; // get employee ID number
...
};
Assume each employee has a unique ID number. It’d make a good set key:
struct IDNumberLess {
bool operator()(Employee const & lhs, Employee const & rhs) const
{ return lhs.idNumber() < rhs.idNumber(); }
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 415

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Be Wary of In-Place Key Modification


using EmpIDSet = std::set<Employee, IDNumberLess>;
EmpIDSet se; // se is sorted by ID number
The set is dependent only on the ID number, so changing other fields is
innocuous:
Employee selectedID;
...
auto i(se.find(selectedID));
if (i != se.end()) {
i->setTitle("Corporate Deity"); // conceptually fine
}
For this code to compile, it must be legal to modify set elements.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 416

Be Wary of In-Place Key Modification


Originally, the Standard was ambiguous about set mutability.
§ Different STL implementations thus did different things:
if (i != se.end()) {
i->setTitle("Corporate Deity"); // compiles under some STL
} // implementations, is rejected
// under others
§ The ambiguity was officially resolved in May 2001.
Æ Keys in associative containers are const.
Æ The code above should therefore not compile.
The motivation is nevertheless valid:
§ It’s perfectly reasonable to want to change an object in a way that
doesn’t affect its location in an associative container.
§ To get the code to compile, a const_cast must be used.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 417

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Modifying Container Elements via Casts


Unfortunately, the cast is easy to write incorrectly.
§ We must cast to a reference.
if (i != se.end()) {
const_cast<Employee&>(*i).setTitle("Corporate Deity");
}
§ These approaches won’t behave as desired:
if (i != se.end()) {
static_cast<Employee>(*i).setTitle("Corporate Deity");
}
if (i != se.end()) {
((Employee)(*i)).setTitle("Corporate Deity");
}
Æ They don’t modify *i, they modify a copy of *i!

Even when the cast works, it’s critical that only non-key parts of the object
are modified.
§ If a key part is modified, the container may become corrupted.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 418

Safe Modification of
Associative Container Elements
A safe, portable, cast-less way to change values in associative containers
works this way:
1. Find the container element you want to change.
2. Make a copy of it.
3. Modify the copy so it has the value you want.
4. Erase the element from the container.
5. Insert the copy into the container.
w Where applicable, use the iterator from Step 1 as a hint to insert.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 419

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Modifying Associative Container Elements


Example:
EmpIDSet se; // se is still a set of employees
// sorted by ID number
Employee selectedID;
...
auto i(se.find(selectedID)); // Step 1: find element to change
if (i != se.end()) {
Employee e{*i}; // Step 2: copy the element
e.setTitle("Corporate Deity"); // Step 3: modify the copy
se.erase(i++); // Step 4: erase the element
se.insert(i, e); // Step 5: insert the copy; if
} // possible, use a location hint
// for efficiency
Recall that insert with an accurate hint runs in amortized constant time.
In Modern C++ we can write i = erase(i)
because erase() returns an interator to
the position following the erased one.
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 420

Guideline
Be wary of in-place key modification.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 421

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Consider sorted vectors for Fast Lookups


Standard associative containers have different lookup complexities:
§ set/multiset/map/multimap offer O(log2 n).
§ unordered (hashed) containers offer O(1).
Sorted vectors also offer O(log2 n) lookup complexity:
§ Via std::binary_search, std::lower_bound, etc.
Sorted vectors often the best choice:
§ Faster (e.g., 35-50% faster)
§ Use less memory

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 422

vectors vs. Associative Containers


Compared to set/multiset/map/multimap:
§ Binary trees.
Æ Nodes add overhead to payload:
w Pointers to children
w Pointer to parent
w Possibly other data (e.g., node color in red-black trees)
§ Nodes may be scattered across memory pages.
Æ Can lead to cache misses, page faults.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 423

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

vectors vs. Associative Containers


Compared to hashed containers:
§ Varying implementations, but
nodes always have overhead:
Æ Pointer to next bucket element.
Æ Possibly pointer to previous
element.
§ Nodes may be scattered across
memory pages.
Æ Can lead to cache misses, page
faults.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 424

vectors vs. Associative Containers


Sorted vectors don’t have nodes:
§ No per-element overhead:
Æ Maybe unused capacity, but can
be ignored for lookup purposes
w Or “shrink to fit” the vector to get rid of it.
§ Elements stored in order in contiguous memory.
Æ Values near one another near each other in memory.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 425

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

vectors vs. Associative Containers


Sorted vectors have a different problem:
§ Insertions and erasures are O(n) instead of O(log2 n)!
§ Insertions and erasures lead to pointer/reference and iterator
invalidation

Associative containers designed for a mixture of:


§ Insertions
§ Erasures
§ Lookups
In software that mixes them, associative containers a good choice.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 426

vectors vs. Associative Containers


Some software divided into phases:
1. Setup: many insertions, maybe some erasures, few lookups.
2. Lookup: many lookups, very few insertions or erasures.
Æ Cost of this phase vastly dominates.

Then sorted vectors a viable candidate:


§ In phase 1, insert data into vector and sort it.
Æ Various approaches are possible, e.g.:
w Insert data, then invoke sort.
w Insert data into set or multiset, then copy into vector.
Æ If duplicates prohibited, maintain constraint manually.
w E.g., invoke unique after sorting.
§ In phase 2, search vector via binary_search or lower_bound.
Æ The next page summarizes these algorithms.
Æ Do insertions in proper location, i.e., maintain sortedness.
w E.g., use lower_bound followed by insert

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 427

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Binary Searches on Sorted vectors


§ binary_search: returns bool, hence of limited utility.
§ lower_bound or upper_bound:
Æ lower_bound(v) returnswhere the first v is or v’s proper insertion
location.
Æ upper_bound(v) returns one past the last v or v’s proper insertion
location.

… 5 6 9 10 10 16 16 18 99 …
lower_bound(10) lower_bound(17)
upper_bound(10) upper_bound(17)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 428

Sorted vector as a set


std::vector<int> v; // emulates a set<int>
... // phase 1: produce sorted
// vector w/o duplicates
std::sort(begin(v), end(v));
v.erase(std::unique(begin(v), end(v)), end(v));
... // phase 2: lookup

int key; // holds value to look up in v


...
// lookup via binary_search
if (std::binary_search(begin(v), end(v), key)) ...
// lookup via lower_bound
std::vector<int>::iterator it;
if ((it = std:: lower_bound(begin(v), end(v), key)) != end(v) && !(key < *it))
...

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 429

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Sorted vector as a map


Design issues:
§ vector holds pair objects (just like map).
§ To sort the vector of pair objects we need a comparator that takes two
pairs and, but only compares the keys (first elements of the pair objects)
§ Predicate used for lookups takes two different parameter types:
Æ One for the pair’s first type (the key being searched for)
Æ One for a pair (the “map” element being compared against)
Æ Parameters may come in either order; both must be handled.
w Two operator() functions in a single function object class.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 430

Sorted vector as a map


Comparison function object template for sorting and lookup:
template <class Key, class Payload>
struct MapCmpFuncs {
using Value = std::pair<Key, Payload>;
// comparison function for doing std::sort()
bool operator()(Value const& p1, Value const& p2) const
{ return p1.first < p2.first; }
// comparison function for doing lookups — variation 1
bool operator()(Value const& p, Key const& key) const
{ return p.first < key; }
// comparison function for doing lookups — variation 2
bool operator()(Key const& key, Value const& p) const
{ return key < p.first; }
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 431

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Keeping Comparison Functions Consistent


Move the real work to one function:
template <class Key, class Payload>
struct MapCmpFuncs {
using Value = std::pair<Key, Payload>;
bool operator()(Value const& p1, Value const& p2) const // for std::sort
{return keyLess(p1.first, p2.first); }
bool operator()(Value const& p, Key const& key) const // for lookups
{ return keyLess(p.first, key); } // variation 1
bool operator()(Key const& key, Value const& p) const // for lookups
{ return keyLess(key, p.first); } // variation 2
private:
bool keyLess(Key const& key1, Key const& key2) const // real work
{ return key1 < key2; } // done here
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 432

Sorted vector as a map


// Handy type aliases
template <class Key, class Payload>
using MapVec<Key, Payload> = std::vector<std::pair<Key, Payload>>;

using IntStringComp = MapCmpFuncs<int, std::string>; // from previous slide

MapVec<int, std::string> m; // emulates a map<int, string> with vector


... // phase 1: produce sorted vector
// w/o duplicates

std::sort(begin(m), end(m), IntStringComp{});


v.erase(std::unique(begin(m), end(m), IntStringComp{}), end(m));

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 433

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Sorted vector as a map


... // phase 2: lookup
int key; // holds value to look up in m
...
// lookup via binary_search
if (std::binary_search(begin(m), end(m), key, IntStringComp{})) ...
// lookup via lower_bound
MapVec::iterator it;
if ((it = std:: lower_bound( begin(m), end(m),
key, IntStringComp{})) != end(m)
&& !IntStringComp{}(key, *it)) ...

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 434

Consider Sorted vectors


Recall lookup complexities:
§ set/multiset/map/multimap offer O(log2 n).
§ So does sorted vector.
§ unordered (hashed) containers offer O(1).
For large enough n, O(1) wins:
§ Several probes in sorted vector could lead to page faults!

§ At some point, hashed containers run faster.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 435

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Boost’s flat_ Containers


Off-the-shelf set/map/multiset/multimap implementations that:
§ Use a sorted vector as underlying storage.
§ Adhere to C++11 interfaces (as much as possible).
Æ E.g., support move operations, emplacement, stateful allocators, etc.
Names are:
§ flat_set
§ flat_map
§ flat_multiset
§ flat_multimap

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 436

Guideline
Know when a sorted vector is superior to a standard associative container.

I strongly recommend measurement if performance is critical.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 437

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Hashed Tables
To avoid backward-compatibility issues and to emphasize the containers’
unsorted nature, the names are
§ unordered_set and unordered_multiset
§ unordered_map and unordered_multimap
These are specified so as to be largely compatible with existing
implementations:
§ E.g., forward iterators are required, but bidirectional ones are allowed.
§ E.g., incremental expansion is allowed, but not required.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 438

Hash Tables
§ Declared in <unordered_set> and <unordered_map>.
Æ Default hashing functionality declared in <functional>.
§ Designed not to conflict with pre-TR1/C++11 implementations.
Æ I.e., hash_set, hash_map, hash_multiset, hash_multimap.
w Interfaces vary – hence the need for standardization.
w Standard names are unordered_set, unordered_map, etc.
Æ Compatible with hash_* interfaces where possible.
§ Each bucket has its own chain of elements:

Conceptual diagrams!
Implementations vary!

§ Bucket count can change dynamically.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 439

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Hash Tables With Duplicate Keys


§ Hash tables are inherently more complicated for users than the
red/black tree based containers.
§ We accept this trade-off for performance.
§ With a good hashing function, hash tables offer constant time for look
ups and adds and removes.
§ The enemy of hash performance is collisions.
ÆA good hashing function has few collisions and bad one has many.
§ Note that unordered_mult* is automatically suspect.
Æ Hashes with duplicate keys imply collisions.
Æ Duplicate keys are not an issue for tree-based containers, but a
design that uses a hash-based container where many keys are
duplicated is likely to have performance issues.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 440

Containers’ Characteristics
§ Usual members exist:
Æ iterator/const_iterator and other type aliases.
Æ begin/end/cbegin/cend, insert/erase, size, swap, etc.
§ Also 3 associative container functions: find, count, equal_range.
Æ lower_bound/upper_bound are absent.
§ unordered_map offers operator[] and at.
§ Most relationals not supported: no <, <=, >=, >
Æ Indeterminate ordering makes these too expensive.
Æ == and != do exist: result based on content, not ordering.
w Expected complexity O(n); worse-case is O(n2).
§ Only forward iteration is provided.
Æ No reverse_iterators, no rbegin/rend/crbegin/crend.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 441

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Hash Table Parameters


Hashing and equality-checking types are template parameters:
template< class Value,
class Hash = std::hash<Value>,
class Pred = std::equal_to<Value>,
class Alloc = std::allocator<Value>>
struct unordered_set { … };
template< class Key,
class T,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<std::pair<const Key, T>>>
struct unordered_map { … };

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 442

Hashing Functions
Defaults are provided for built-in, string, and smart pointer types:
struct Widget { … };
std::unordered_set<int> si; // all use default hash
std::unordered_multiset<double> md; // func for shown types
std::unordered_map<std::wstring, int>mwi;
std::unordered_multimap<Widget*, std::string> w; // May not do what you
// want!
std::unordered_multimap<std::unique_ptr<Widget>, std::string> mm2;
Also for these less commonly used types:
§ std::vector<bool>
§ std::bitset
§ std::thread::id
§ std::error_code
§ std::type_index

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 443

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Hashing Functions
To override a default or hash a UDT, specialize hash<T> or create a custom
function object:
template<>
struct std::hash<Widget> {
using argument_type = Widget;
using result_type = std::size_t;
std::size_t operator()(Widget const& w) const { … };
};
std::unordered_set<Widget> sw;

struct IntHasher {
std::size_t operator()(int i) const { … };
};
std::unordered_map<int, std::string, IntHasher> mis;

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 444

Operations for Bucket Count and Load Factor


Constructors allow a floor on bucket count (B) to be specified:
std::unordered_set<int> s1; // B chosen by implementation
std::unordered_set<int> s2(53); // B >= 53. (Other ctor forms
// support bucket floor, too.)
A table’s load factor (z) is the average number of elements/bucket:
§ z = container.size()/B.
§ z can be queried, and a ceiling for it can be “hinted” (requested):
float z{s1.load_factor()}; // get current load factor
s1.max_load_factor(0.75f); // request ceiling for z;
// future insertions may
// increase B so that z <= .75,
// then rehash s1 to use new B
float z_max{s1.max_load_factor()}; // get current zmax (defaults to 1)
Because max_load_factor(z) is only a request, it’s possible that
container.load_factor() > container.max_load_factor().

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 445

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Rehashing
Explicit rehashing can also change the bucket count and load factor.
§ Specify number of desired buckets via rehash.
§ Specify number of expected elements via reserve.
std::unordered_set<int> s;
...
auto newB(computeNewB());
s.rehash(newB); // reorganize s so that B >= newB
// and s.size()/B <= zmax
...
auto expElems(computeExpectedElements());
s.reserve(expElems); // reorganize s so that expElems/B <= zmax
Rehashing (implicitly or explicitly) invalidates iterators.
§ But not pointers or references.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 446

Iterating Over Bucket Contents


Useful for e.g., monitoring performance of hashing functions.
std::unordered_set<std::string> s;

auto numBuckets(s.bucket_count()); // # buckets
for (std::size_t b{0}; b < numBuckets; ++b) {
std::cout << "Bucket " << b << " has "
<< s.bucket_size(b) // # elems in bucket b
<< " elements: ";
std::copy(
s.cbegin(b), s.cend(b), // iters for bucket b
std::ostream_iterator<std::string>{std::cout, " ”}
);
std::cout << '\n';
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 447

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Hash Tables Summary


§ Unordered containers based on hash tables with open hashing.
§ Only forward iteration is supported.
§ Maximum load factor can be dynamically altered.
§ There is support for iterating over individual buckets.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 448

Guideline
Familiarize yourself with the hashed containers.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 449

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Associative Container Summary


§ Always have comparison functions return false for equal values.
§ Be wary of in-place key modification.
§ Know when a sorted vector is superior to a standard associative
container.
§ Familiarize yourself with the hashed containers.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 450

Alex Stepanov on Container Selection


§ Use vector.

Bjarne concurs.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 451

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Alex Stepanov on the STL


§ The design of STL containers was based on factors that are no longer
true.
Æ malloc() is now more expensive than it used to be.
Æ All memory access are no longer equal.

§ Node-based containers are less attractive than they used to be.


§ Consider btree.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 452

Generalized Function Objects

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 453

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

From TR1: Generalized Function Objects


Motivation:
§ Function pointers and member function pointers are rigid:
Æ Exact parameter/return types and ex. specs. must be specified.
Æ Can point to only one of static and nonstatic member functions.
Æ Can’t point to function objects.

§ Useful to be able to refer to any callable entity compatible with a given


calling interface.
Æ Convenient for developers (especially for callbacks).
Æ Can help limit code bloat from template instantiations.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 454

Callable Entities
Something that can be called like a function:
§ Functions, function pointers, function references:
void f(int x); // function
void (*fp)(int) {f}; // function pointer
int val;

f(val); // call f
fp(val); // call *fp

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 455

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Callable Entities
§ Objects implicitly convertible to one of those:
struct Widget {
using FuncPtr = void (*)(int);
operator FuncPtr() const; // conversion to function ptr

};
Widget w; // object with conversion to func ptr
int val;

w(val); // “call” w, i.e.,
// invoke w.operator FuncPtr()

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 456

Callable Entities
§ Function objects (including closures):
struct Gadget {
void operator()(int); // function call operator

};
Gadget g; // object supporting operator()
int val;

g(val); // “call” g, i.e., invoke w.operator()

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 457

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

std::function Basics
§ Declared in <functional>.
§ std::functions are type-safe wrappers for callable entities:
std::function<int(std::string&)> f; // f refers to callable entity
// compatible with given sig.

int someFunc(std::string&); // some function


f = someFunc; // f refers to someFunc
struct Gadget {
int operator()(std::string&); // function call operator

};
Gadget g;
f = g; // f refers to g

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 458

Fundamental Behavior
std::function object

Arguments Return Value


Callable Entity
from caller to caller
declared declared
std::function std::function
parameters return type

int someFunc(std::string&); // some function


f = someFunc;
std::string str;
...
int result{f(str)}; // calls someFunc

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 459

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Compatible Signatures
A callable entity is compatible with a function object if:
§ The function object’s parameter types can be implicitly converted to the
entity’s parameter types.
§ The entity’s return type can be implicitly converted to the function
object’s.

std::function object

Arguments Return Value


Callable Entity
from caller to caller

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 460

function Callback Example


A Button class supporting click-event callbacks:
§ The callback parameter indicates a down- or up-click.
struct Button: SomeGUIFrameworkBaseClass {

using CallbackType = std::function<void(short)>;
void setCallback(CallbackType const& cb)
{
clickHandler = cb;
}
virtual void onClick(short upOrDown) // invoked by base class
{
clickHandler(upOrDown); // invoke function object
}
private:
CallbackType clickHandler;
};
In ”real” code, we’d
compare clickHandler with
nullptr before calling.
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 461

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

function Callback Example


void buttonClickHandler(int eventType); // non-member function
struct ButtonHandler {

static int clicked(short upOrDown); // static member function
};
void (*clicker)(int){buttonClickHandler}; // function pointer
Button b;

b.setCallback(buttonClickHandler); // pass non-member func
b.setCallback(ButtonHandler::clicked); // pass static member func
b.setCallback(clicker); // pass function ptr
Note the (compatible) type mismatches:
§ buttonClickHandler and clicker take int, not short
§ ButtonHandler::clicked returns int, not void

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 462

function Callback Example


struct ButtonClickCallback { // class generating
void operator()(short upOrDown) const; // function objects
};
Button b;

ButtonClickCallback bccb;
b.setCallback(bccb); // pass function object
void logClick(short upOrDown);
...

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 463

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Other function Characteristics


§ Declared in <functional>
§ Supports nullness testing:
std::function<signature> f;

if (f) … // fine
if (f == nullptr) … // also fine
§ Disallows equality and inequality testing
Æ Nontrivial to determine whether two function objects refer to equal
callable entities.
std::function<signature> f1, f2;

if (f1 == f2) … // error!
if (f1 != f2) … // error!
§ The target member function of std::function can be used to the get the
address of the callable, but its type must be known at compile time.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 464

function Summary
§ function objects are generalizations of function pointers.
§ Can refer to any callable entity with a compatible signature.
§ Especially useful for callback interfaces.
§ Explicitly disallow tests for equality or inequality.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 465

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Lambda Expressions

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 466

Lambda Expressions
A quick way to create function objects at their point of use.
std::vector<int> v;

auto it(std::find_if( v.cbegin(), v.cend(),
[](int i) { return i > 0 && i < 10; }));
Essentially generates:
struct MagicType1 {
bool operator()(int i) const { return i > 0 && i < 10; }
};
auto it(std::find_if( v.cbegin(), v.cend(), MagicType1{}));

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 467

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Lambda Expressions
Another example:
using SPWidget = std::shared_ptr<Widget>;
std::deque<SPWidget> d;

std::sort(begin(d), end(d),
[](SPWidget const& sp1, SPWidget const& sp2)
{ return *sp1 < *sp2; });
Essentially generates:
struct MagicType2 {
bool operator()(SPWidget const& p1, SPWidget const& p2) const
{ return *p1 < *p2; }
};
std::sort(begin(d), end(d), MagicType2{});
Function objects created through lambda expressions are closures.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 468

Variable References in Lambdas


Closures may outlive their creating function:
std::function<bool(int)> returnClosure(int a) // return type to be
{ // discussed soon
int b, c;
...
return[](int x) // won’t compile, but
{ return a*x*x + b*x + c == 0; }; // assume it would
}
auto f(returnClosure(10)); // f is essentially a
// copy of λ’s closure
In this call,
if (f(22)) ... // invoke the closure
what are the values of a, b, c?
§ returnClosure no longer active!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 469

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Variable References in Lambdas


This version has no such problem:
int a; // now at global or
// namespace scope
std::function<bool(int)> returnClosure()
{
static int b, c; // now static
...
return[](int x) // now compiles
{ return a*x*x + b*x + c == 0; };
}
auto f(returnClosure()); // as before
...
if (f(22)) ... // as before
a, b, c outlive returnClosure’s invocation.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 470

Variable References in Lambdas


Rules for variables lambdas may refer to:
§ Non-static locals referenceable only if “captured.”
std::function<bool(int)> returnClosure(int a)
{
int b, c;
...
return [](int x){ return a*x*x + b*x + c == 0; }; // to compile, must
} // capture a, b, c;
// this example
// won’t compile
§ Variables of static storage duration always referenceable.
int a;
std::function<bool(int)> returnClosure()
{
static int b, c;
...
return [](int x){ return a*x*x + b*x + c == 0; }; // no need (& not
} // permitted) to
// capture a, b, c

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 471

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Capturing Local Variables


Capturing locals puts copies in closures:
{
int minVal;
double maxVal;

auto it(std::find_if( v.cbegin(), v.cend(),
[minVal, maxVal](int i)
{ return i > minVal && i < maxVal; }));
}
Essentially corresponds to:
struct MagicType {
MagicType(int v1, double v2): _minVal{v1}, _maxVal{v2} {}
bool operator()(int i) const { return i > _minVal && i < _maxVal; }
private:
int _minVal;
double _maxVal;
};
auto it(std::find_if( v.cbegin(), v.cend(), MagicType{minVal, maxVal}));

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 472

Capturing Local Variables


Captures may also be by reference:
{
int minVal;
double maxVal;

auto it(std::find_if( v.cbegin(), v.cend(),
[&minVal, &maxVal](int i)
{ return i > minVal && i < maxVal; }));
}
Essentially corresponds to:
struct MagicType {
MagicType(int& v1, double& v2): _minVal{v1}, _maxVal{v2} {}
bool operator()(int i) const { return i > _minVal && i < _maxVal; }
private:
int& _minVal;
double& _maxVal;
};
auto it(std::find_if( v.cbegin(), v.cend(), // same as
MagicType{minVal, maxVal})); // before

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 473

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Capturing Local Variables


Different (non-static) locals may be captured differently:
{
int minVal;
double maxVal;

auto it(std::find_if( v.cbegin(), v.cend(),
[minVal, &maxVal](int i)
{ return i > minVal && i < maxVal; }));
}
Essentially corresponds to:
struct MagicType {
MagicType(int v1, double& v2): _minVal{v1}, _maxVal{v2} {}
bool operator()(int i) const { return i > _minVal && i < _maxVal; }
private:
int _minVal;
double& _maxVal;
};
auto it(std::find_if( v.cbegin(), v.cend(), // same as
MagicType{minVal, maxVal})); // before

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 474

Capturing Local Variables


Capture mode defaults may be specified:
auto it(std::find_if( v.cbegin(), v.cend(), // default is
[=](int i) // by value
{ return i > minVal && i < maxVal; }));
auto it(std::find_if( v.cbegin(), v.cend(), // default is
[&](int i) // by ref
{ return i > minVal && i < maxVal; }));
With a default capture mode, captured variables need not be listed.
§ As in examples above.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 475

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Capturing Local Variables


Default overridable on a per-variable basis:
auto it(std::find_if( v.cbegin(), v.cend(), // default capture is
[=, &maxVal](int i) // by value, but maxVal
{ return i > minVal && // is by reference
i < maxVal; }));
Essentially corresponds to:
struct MagicType {
MagicType(int v1, double& v2): _minVal{v1}, _maxVal{v2} {}
bool operator()(int i) const { return i > _minVal && i < _maxVal; }
private:
int _minVal;
double& _maxVal;
};
auto it(std::find_if( v.cbegin(), v.cend(), MagicType{minVal, maxVal)}));

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 476

Capturing Struct Members


To access struct members within a member function, capture this:
struct Widget {
void doSomething();
private:
std::list<int> li;
int minVal;
};
void Widget::doSomething() {
auto it(std::find_if( li.cbegin(), li.cend(), // error! attempt
[minVal](int i) { return i > minVal; } // to capture
)); // “this->minVal”

}
void Widget::doSomething() {
auto it(std::find_if( li.cbegin(), li.cend(),
[this](int i) { return i > minVal; } // fine
)); // (“minVal” ⇒
… // “this->minVal”)
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 477

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Capturing Struct Members


A default capture mode also makes this available:
struct Widget {
void doSomething();
private:
std::list<int> li;
int minVal;
};
void Widget::doSomething() {
auto it(std::find_if( li.cbegin(), li.cend(),
[=](int i) { return i > minVal; } // fine, copies
)); // “this” into closure

}
void Widget::doSomething() {
auto it(std::find_if( li.cbegin(), li.cend(),
[&](int i) { return i > minVal; } // also fine, holds
)); // ref to “this” in
… // closure
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 478

Avoid Default Capture Modes


Executive summary:
§ Default by-reference capture: can lead to dangling references.
§ Default by-value capture:
Æ Seemingly prevents dangling references, but doesn’t.
Æ Seemingly guarantees self-contained closures, but doesn’t.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 479

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Default By-Reference Capture


Given
using FilterContainer =
std::vector<std::function<bool(int)>>;
FilterContainer filters; // filtering funcs
consider:
void addDivisorFilter()
{
auto calc1(computeSomeValue1());
auto calc2(computeSomeValue2());
auto divisor(computeDivisor(calc1, calc2));
filters.emplace_back(
[&](int value) { return value % divisor == 0; }
);
}
filters contains dangling reference after addDivisorFilter returns.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 480

Default By-Reference Capture


Same problem exists with explicit capture,
void addDivisorFilter()
{
... // as before
filters.emplace_back(
[&divisor](int value) { return value % divisor == 0; }
);
}
but it’s clearer what needs to be checked to avoid dangle problems.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 481

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Default By-Reference Capture


One-shot-use lambdas are technically safe:
template<class C>
void workWithContainer(C const& container)
{
auto calc1(computeSomeValue1());
auto calc2(computeSomeValue2());
auto divisor(computeDivisor(calc1, calc2));
using ContElemT = typename C::value_type;
if (std::all_of(
std::begin(container), std::end(container),
[&](ContElemT const& value) // no dangling
{ return value % divisor == 0; }) // in this case
){

} else {

}
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 482

Default By-Reference Capture


But copy/paste can change the context and lead to dangling.
§ E.g., to add the lambda to filters.
Explicit captures simply better software engineering.
§ Akin to function parameters being preferable to globals.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 483

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Default By-Value Capture


Doesn’t preclude dangling:
§ Captured pointers can still dangle.
Default by-value capture leads to false sense of security.
§ Especially if you use only smart pointers.
Æ You don’t :-)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 484

Default By-Value Capture


A lambda in a member function using default by-value capture:
struct Widget {

void addFilter() const; // add entry to filters
private:
int divisor;
};
void Widget::addFilter() const
{
filters.emplace_back(
[=](int value) { return value % divisor == 0; }
);
}
§ Looks safe.
§ Looks self-contained.
§ Isn’t.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 485

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Default By-Value Capture


Only non-static local variables can be captured.
§ divisor isn’t local.
In a member function, divisor is really this->divisor.
§ this is local.
Æ What’s captured is this!
From capture perspective, compilers do the following:
void Widget::addFilter() const
{
auto currentObjectPtr(this);
filters.emplace_back(
[currentObjectPtr](int value)
{ return value % currentObjectPtr->divisor == 0; }
);
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 486

Default By-Value Capture


But this can dangle:
void doSomeWork()
{
auto pw(std::make_unique<Widget>()); // create Widget

pw->addFilter(); // add filter using


// Widget::divisor
...
} // destroy Widget
When doSomeWork returns, the added filter holds a dangling pointer.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 487

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Default By-Value Capture


A solution is to truly copy what you want to capture:
void Widget::addFilter() const
{
auto divisorCopy(divisor);
filters.emplace_back(
[divisorCopy](int value)
{ return value % divisorCopy == 0; }
);
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 488

Default By-Value Capture


This approach can be used with a default by-value capture, too,
void Widget::addFilter() const
{
auto divisorCopy(divisor);
filters.emplace_back(
[=](int value)
{ return value % divisorCopy == 0; }
);
}
but this isn’t as clear.
§ Default by-value capture is what led to the problem in the first place!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 489

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Default By-Value Capture


Consider a revised version of addDivisorFilter:
void addDivisorFilter()
{
static auto calc1(computeSomeValue1()); // now static
static auto calc2(computeSomeValue2()); // now static
static auto divisor(computeDivisor(calc1, calc2)); // now static
filters.emplace_back(
[=](int value){ return value % divisor == 0; }
);
++divisor;
}
§ divisor’s not captured!
Æ Only non-static locals can be captured.
Æ divisor’s static.
Æ Incrementing divisor affects behavior of closure in filters!

Default by-value need not imply that a closure is self-contained.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 490

Capture Guidance
Explicitly list variables to capture.
§ Compilers reject un-capturable variables.
Æ Struct data members.
Æ Variables of static storage duration.
§ Reduces risk of dangling captures.
§ Reduces false sense of security regarding closures being self-contained.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 491

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guideline
Avoid default capture modes.

Based on EMC++ Item 31.


Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 492

Lambda Return Types


Optional when:
§ Return type is void.
§ Lambda body is “return expr;” (relaxed in C++14)
Æ Return type is that of expr.
Otherwise must be specified via trailing return type syntax:
std::vector<double> v;

std::transform( begin(v), end(v), begin(v),
[](double d)->double
{
makeLogEntry("std::transform", d);
return std::sqrt(std::abs(d));
});

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 493

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Trailing Return Types


§ Must be used with lambdas (when a return type is given).
§ Often useful with decltype (described later).
§ Permitted for any function (with a leading auto):
void f(int x); // traditional syntax
auto f(int x)->void; // same declaration with trailing
// return type
struct Widget {
void mf1(int x); // traditional
auto mf2() const -> bool; // trailing return type
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 494

Template Challenge
Let’s write a function template from scratch.
Don’t worry it’s the simplest function in the world: add()—it adds two things:
template <class T>
T add(T a, T b)
{ return a + b; }
T is an unknown type so it might be expensive to copy, so:
template <class T>
T add(T const& a, T const& b)
{ return a + b; }
Easy-peasy, right? The simplest function in the world.
Wait, but if we want to add two things of different types?
template <class T, class U>
T add(T const& a, U const& b)
{ return a + b; }

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 495

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Template Challenge
What is wrong with this?
template <class T, class U>
T add(T const& a, U const& b)
{ return a + b; }

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 496

Template Challenge
What is wrong with this?
template <class T, class U>
T add(T const& a, U const& b)
{ return a + b; }
What should the return type be?
• We can’t assume it is the type of the first parameter.
• We can’t even assume it is the type of either parameter.
• Adding a char and a short results in an int.
The return type should be whatever you get when you add a T and a U.
How do we say that?
template <class T, class U>
decltype(T + U) add(T const& a, U const& b)
{ return a + b; }

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 497

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Template Challenge
What is wrong with this?
template <class T, class U>
decltype(T + U) add(T const& a, U const& b)
{ return a + b; }
It doesn’t compile! Why not?
decltype works on expressions. You can’t add two types.
How can we fix this?
template <class T, class U>
decltype(a + b) add(T const& a, U const& b)
{ return a + b; }

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 498

Template Challenge
What is wrong with this?
template <class T, class U>
decltype(a + b) add(T const& a, U const& b)
{ return a + b; }
It doesn’t compile! Why not?
a and b in decltype(a + b) are not defined.
How can we fix this?
template <class T, class U>
auto add(T const& a, U const& b) -> decltype(a + b)
{ return a + b; }

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 499

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Template Challenge
This is the simplest template in the world, but it couldn’t be written in
Classic C++ because we need decltype and trailing return type function
declarations.
template <class T, class U>
auto add(T const& a, U const& b) -> decltype(a + b)
{ return a + b; }

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 500

Lambdas without Parameter Lists


Lambdas without parameters may omit the parameter list.
§ Such functions especially useful with threads:
void doWork(int x, int y);
void doMoreWork();
std::thread t1{[]() { doWork(10, 20); doMoreWork(); }}; // w/empty
// param list
std::thread t2{[] { doWork(10, 20); doMoreWork(); }}; // w/o empty
// param list
Omitting the optional parentheses seems to be common.
Minimum legal lambda expression: A paper currently before
the committee proposes
[]{} templated lambdas with
Optionally: an optional set of angle
brackets for template
[](){} parameters:
To call this we would write: [] <> () {}
To call the closure:
[](){}() [] <> () {} ()
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 501

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Lambda Expression Complexity


Lambdas may be arbitrarily complex:
§ Multiple statements, multiple returns.
§ Throw/catch exceptions.
§ Essentially anything allowed in a “normal” function.
Maintainability considerations suggest:
§ Short, clear, context-derived lambdas are best.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 502

Storing Closures
Closure types not specified, but two easy ways to store closures:
§ auto:
auto multipleOf5([](long x) { return x % 5 == 0; });
std::vector<long> vl;

vl.erase(std::remove_if(begin(vl), end(vl), multipleOf5), end(vl));
§ std::function:
std::function<bool(long)> multipleOf5{ // see next page for syntax
[](long x) { return x % 5 == 0; }};

vl.erase(std::remove_if(begin(vl), end(vl), multipleOf5), end(vl));

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 503

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Specifying Function Types


A function’s type is its declaration w/o any names:
void f1(int x); // type is void(int)
double f2(int x, std::string& s); // type is double(int, std::string&)
C++ uses function types for e.g., std::function:
std::function<bool(long)> multipleOf5{ // from prior slide
[](long x) { return x % 5 == 0; }};
Trailing return type syntax is equivalent:
std::function<auto (long)->bool> multipleOf5{ // equivalent to
[](long x) { return x % 5 == 0; }}; // above

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 504

Storing Closures
auto more efficient than std::function, but not always applicable.
§ Not allowed for function parameters or return types:
void useIt(auto func); // error!
void useIt(std::function<bool(long)> func); // fine
template<class Func>
void useIt(Func func); // fine, but generates
// multiple functions

auto makeFunc(); // error in C++11


std::function<bool(long)> makeFunc(); // fine
template<class Func>
Func makeFunc(); // fine, but generates
// multiple functions,
// and callers must
// specify Func

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 505

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Storing Closures
§ Not allowed for struct data members:
struct Widget {
private:
auto func; // error!
...
};
struct Widget {
private:
std::function<bool(long)> f; // fine
...
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 506

Stored Closures and Dangling References


Stored closures can hold dangling members.
§ E.g., pointers to deleted heap objects.
§ E.g., references to beyond-scope locals:
std::vector<long> vl;
std::function<bool(long)> f;
{ // some block
int divisor;

f = [&](long x) { return x % divisor == 0; }; // closure refers
… // to local var
} // local var’s
// scope ends
vl.erase( std::remove_if(begin(vl), end(vl), f), // calls to f use
end(vl)); // dangling ref!
It’s your responsibility to avoid such problems.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 507

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Stored Closures and Dangling References


In member functions, even “[=]” capture can lead to dangles.
§ Struct data members aren’t copied, this is:
struct Widget {
...
std::function<int (int)> numberCruncher()
{ return [=](int x) { return x * myVal; }; } // copies “this”, not myVal!
private:
int myVal;
};
std::unique_ptr<Widget> pw{new Widget}; // create Widget
...
auto f(pw->numberCruncher()); // save closure
...
pw = nullptr; // delete Widget
...
auto value(f(22)); // undefined! f refers to
// deleted data

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 508

Stored Closures and Dangling References


Copy-capturing local copies of data members avoids this problem:
struct Widget {
std::function<int (int)> numberCruncher()
{
auto localMyVal(myVal); // copy myVal to local
return [=](int x) { return x * localMyVal; }; // copy local into closure
}
...
};
std::unique_ptr<Widget> pw{new Widget}; // as before
...
auto f(pw->numberCruncher()); // as before
...
pw = nullptr; // as before
...
auto value(f(22)); // now well-defined

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 509

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Lambdas in C++14
“Polymorphic” parameters permitted:
std::vector<std::shared_ptr<Widget>> vspw;

std::sort( begin(vspw), end(vspw),
[]( std::shared_ptr<Widget> const& p1, // C++11
std::shared_ptr<Widget> const& p2)
{ return *p1 < *p2; });
std::sort( begin(vspw), end(vspw),
[]( auto const& p1, auto const& p2) // C++14
{ return *p1 < *p2; });
Generated structs have templatized operator()s:
struct MagicType {
template<class T1, class T2>
bool operator()(T1 const& p1, T2 const& p2) const
{ return *p1 < *p2; }
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 510

Lambdas in C++14
Return type may be deduced, even with multiple statements:
std::vector<double> v;

std::transform( begin(v), end(v), begin(v), // C++11
[](double d)->double // (as before)
{
makeLogEntry("std::transform", d);
return std::sqrt(std::abs(d));
});
std::transform( begin(v), end(v), begin(v), // C++14
[](double d) // (note no
{ // return type)
makeLogEntry("std::transform", d);
return std::sqrt(std::abs(d));
});

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 511

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Deduced Return Types in C++14


Return type deduction valid for all functions.
§ Leading auto still required for non-lambdas.
auto f(int x); // C++14; return type to be deduced
struct Widget {
auto mf() const; // C++14; return type to be deduced

private:
int x, y;
};
Deduction based on function definition:
auto f(int x) { counter += x; } // no return stmt ⇒
// return type is void
auto Widget::mf() const
{ return x + y; } // return type is int

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 512

Deduced Return Types in C++14


Multiple returns permitted if same type deduced for each:
auto magicValue1(int seed) // fine; both returns are int
{
if (seed >= 0) return seed;
else return 42;
}
auto magicValue2(int seed) // error! different return types
{
if (seed >= 0) return seed;
else return 4.2;
}
std::deque<long> d;

auto it(std::partition( begin(d), end(d), // fine
[] (auto val)
{ if (val % 7 == 0) return true;
if (val % 5 == 0) return true;
return false; }));

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 513

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Move Capture in C++14


No way to move objects into closures in C++11:
struct Widget{} // expensive to copy

std::function<void()> getFunction()
{
std::vector<Widget> vw;
… // populate vector
return [vw] { … }; // expensive to copy
}
std::function<void()> getFunction()
{
std::vector<Widget> vw;
… // populate vector
return [&vw] { … }; // inexpensive, but
} // getFunction returns
// dangling reference

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 514

Move Capture in C++14


No problem in C++14:
std::function<void()> getFunction()
{
std::vector<Widget> vw;
… // populate vector
return[my_vw{std::move(vw)}] // move pw into closure;
{ … }; // C++14 only
}
Generated struct:
struct MagicType {
MagicType(std::vector<Widget>&& vw)
: my_vw{std::move(vw)} {}
void operator()() const { … }
private:
std::vector<Widget> my_vw;
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 515

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Init Captures in C++14


Feature actually much more general:
§ Arbitrary declaration/initialization of closure data members.
int computeMinVal(int seed);
int computeMaxVal(int seed);
std::function<bool(int)>
compFunc(int minSeed, int maxSeed)
{
return [ minVal{computeMinVal(minSeed)},
maxVal{computeMaxVal(maxSeed)}] (int x)
{ return minVal <= x && x <= maxVal; };
}
§ “Move capture” doesn’t really exist.
Æ Above, neither minVal nor maxVal are local variables.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 516

Init Captures in C++14


For
return[ minVal{computeMinVal(minSeed)}, // from
maxVal{computeMaxVal(maxSeed)}] (int x) // previous
{ return minVal <= x && x <= maxVal; }; // slide
generated code is:
struct MagicType {
MagicType(int min, int max)
: minVal{min}, maxVal{max} {}
bool operator()(int x) const { return minVal <= x && x <= maxVal; }
private:
int minVal, maxVal;
};

return MagicType{ computeMinVal(minSeed),
computeMaxVal(maxSeed)};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 517

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Init Captures in C++14


Init capture of reference members also possible:
// assume defaultMinVal/defaultMaxVal defined elsewhere
return[ &minVal{defaultMinVal}, &maxVal{defaultMaxVal}] (int x)
{ return minVal <= x && x <= maxVal; };
Generated code:
struct MagicType {
MagicType(int& min, int& max)
: minVal{min}, maxVal{max} {}
bool operator()(int x) const { return minVal <= x && x <= maxVal; }
private:
int &minVal, &maxVal;
};

return MagicType{ computeMinVal(minSeed),
computeMaxVal(maxSeed)};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 518

Emulating Init Capture


For pre-C++14 compilers, two workarounds possible:
§ Write code by hand.
Æ Lambdas have no special expressive power.
Æ Anything lambdas do can be hand-coded.
§ Use std::bind for “init capture” and pass bound object to lambda.
Æ We won’t be covering std::bind in this course.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 519

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Emulating Init Capture


The “write code by hand” approach:
struct IsValAndArch { // "is validated and archived"

using DataType = std::unique_ptr<Widget>; // type to


// "capture"
explicit IsValAndArch(DataType&& ptr) // initializer for
: pw{std::move(ptr)} {} // "captured" data
bool operator()() const // body of
{ return pw->isValidated() // would-be
&& pw->isArchived(); } // lambda
private:
DataType pw;
};
auto func(IsValAndArch(std::make_unique<Widget>()));
More work than writing a lambda, but not difficult.

Types with move-only members (such


as unique_ptr) can’t be copied.
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 520

Closures as Function Pointers


Capture-less closures implicitly convert to function pointers:
int (*fp)(int){[](int x) { return x * x; }};
Such closures can be treated like functions.
§ No need for std::function to refer to them.
§ No captures ⇒ no stored pointers or references ⇒ no dangling.
§ Often useful for callbacks with C-like APIs:
int atexit(void (*f)()) noexcept; // from <cstdlib>
std::atexit([]{ logMsg("Shutting down..."); });
Æ In C++11, function pointer linkage not specified ⇒ possible linkage
problems.
w C++14 specifies C++ linkage.
Æ noexcept akin to throw(), but enables more optimizations.
w Violated noexcept ⇒ terminate.
w Exception specifications now deprecated.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 521

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Closures as Function Pointers


Capture-less closures implicitly convert to function pointers:
int (*fp)(int){[](int x) { return x * x; }};

Generated code:
struct MagicType
{
static int __lambda_call_operator_invoker(int x) {return x * x;}
int operator()(int x) const { return __lambda_call_operator_invoker(x); }
using __fptr_t = int(*)(int);
operator __fptr_t() const {return &__lambda_call_operator_invoker;}
};

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 522

Lambdas as const Initializers


Excellent as simple multi-statement initializers for consts:
auto const sortedInts([]()->std::vector<int>
{ // init const vector
std::vector<int> v(kNumValues); // w/kNumValues
std::iota(begin(v), end(v), 0 - kNumValues/2); // sequential ints
return v; // centered at 0
}());

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 524

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Lambdas as const Initializers


Return types can be deduced in C++14:
auto const sortedInts([]
{ // init const vector
std::vector<int> v(kNumValues); // w/kNumValues
std::iota(begin(v), end(v), 0 - kNumValues/2); // sequential ints
return v; // centered at 0
}());

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 526

Lambda/Closure Summary
§ Lambda expressions generate closures.
§ Calling state can be captured by value or by reference.
Æ Avoid default capture modes
§ Return types, when specified, use trailing return type syntax.
§ Closures can be stored using auto or std::function.
Æ Be alert for dangling references/pointers in stored closures.
§ Short, clear, context-derived lambdas are best.
§ C++14 adds support for auto parameters, init captures, and less
restrictive return type deduction.
Æ To emulate init capture for moving into lambdas in C++11, hand
write a function object.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 527

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

RAII and Smart Pointers

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 528

Why Smart Pointers?


Raw pointers are error-prone:
§ Pointer to object or pointer to array? Declaration fails to distinguish.
§ Does pointer own pointee? Declaration doesn’t indicate.
§ For owning pointers, how destroy object?
Æ delete, delete[], free, or some other function?
§ Owned objects must be destroyed exactly once along all paths (including
exceptions).
§ Does pointer dangle? No way to tell.
Smart pointers can address all these issues.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 529

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Use std::unique_ptr for Exclusive-Ownership


Resource Management
The smart pointer of first resort.
§ By default, should be same size as raw pointer.
§ For most operations (e.g., dereferencing), as efficient as raw pointers.
§ Move-only; copying not permitted.
Æ Moving transfers ownership.
std::unique_ptr<Widget> pw1{std::make_unique<Widget>()};
...
auto pw2(pw1); // error! pw1 uncopyable
auto pw2(std::move(pw1)); // okay, pw2 points to Widget,
// pw1 is null
...
pw1 = pw2; // error! pw2 uncopyable
pw1 = std::move(pw2); // fine, pw1 points to Widget,
// pw2 is null

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 530

std::unique_ptr
Good choice for factory function returns:
struct Investment { … };

struct Stock: Investment


Investment { … };

struct Bond:
Investment { … };
Stock Bond RealEstate
struct RealEstate:
Investment { … };

template<class... Ts> // factory function


std::unique_ptr<Investment>
makeInvestment(Ts&&... params);

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 531

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

std::unique_ptr
Simple client use:
{

auto pInvestment(makeInvestment(arguments));

} // *pInvestment destroyed
Works well with ownership chains:
1. Move from factory into local variable.
2. Move from local variable into container.
3. Move from container into object data member.
4. Object is destroyed.
If chain disrupted, pointed-to-object (usually) automatically destroyed.

The “usually” is due to cases of


abnormal program termination.
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 532

Deleters
Deleter is function invoked when pointee is destroyed.
§ Default is delete.
§ Custom deleter can override default.
Æ Type of std::unique_ptr includes type of deleter:
auto delInvmt([](Investment* pInvestment) // custom
{ // deleter
makeLogEntry(pInvestment);
delete pInvestment;
});
template<class... Ts> // revised
std::unique_ptr<Investment, decltype(delInvmt)> // return
makeInvestment(Ts&&... params); // type

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 533

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Deleters
template<class... Ts> // from
std::unique_ptr<Investment, decltype(delInvmt)> // previous
makeInvestment(Ts&&... params) // slide
{
std::unique_ptr<Investment, decltype(delInvmt)> // ptr to be
pInv{nullptr, delInvmt}; // returned

if (a Stock object should be created)


{
pInv.reset(new Stock{std::forward<Ts>(params)...});
}
else if (a Bond object should be created)
{
pInv.reset(new Bond{std::forward<Ts>(params)...});
}
else if (a RealEstate object should be created)
{
pInv.reset(new RealEstate{std::forward<Ts>(params)...});
}
return pInv;
}
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 534

Deleters
auto return type permits deleter to be encapsulated inside factory:
template<class... Ts> // C++14
auto makeInvestment(Ts&&... params) // only
{
auto delInvmt([](Investment* pInvestment) // now
{ // inside
makeLogEntry(pInvestment); // factory
delete pInvestment;
});
std::unique_ptr<Investment, decltype(delInvmt)> // as before
pInv{nullptr, delInvmt};
... // make pInv
// point to
// suitable
// object
return pInv; // as before
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 535

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Deleters
delInvmt takes pointer to base class:
auto delInvmt([](Investment* pInvestment)
{
makeLogEntry(pInvestment);
delete pInvestment;
});
Investment must thus have virtual destructor:
struct Investment {
… // essential
virtual ~Investment(); // design
… // component!
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 536

Deleters and Size of std::unique_ptr


Default deleter should take no space.
§ std::unique_ptr should store only raw pointer to pointee.
§ sizeof(such std::unique_ptrs) should equal sizeof(raw pointer).
Ditto for stateless lambda:
§ No captures ⇒ no state ⇒ no need for memory.
Æ E.g., delInvmt.

std::unique_ptr<T>
T* pointee

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 537

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Deleters and Size of std::unique_ptr


Deleter function ⇒ std::unique_ptr must store pointer to deleter.
§ Doubles size of std::unique_ptr.
T* pointee
void (*)(T*) deleter

Stateful function object (e.g., from a lambda) ⇒ std::unique_ptr must store


object state (e.g., closure).
§ Can make std::unique_ptr arbitrarily large.

T* pointee

deleter
state

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 538

Deleters and Inlining


Inlinability of calls to deleter:
§ Lambdas: easy.
Æ operator() on closure class implicitly inline.
§ Other function objects: typically easy.
Æ Partially dependent on operator() definition in class.
w Common for them to be inline.
§ Functions: depends on compiler.
Æ Inlining through function pointers traditionally uncommon.
w Becoming more common for build-time-known addresses.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 539

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Stateless Lambdas vs. Normal Functions


Stateless lambdas hence preferable to normal functions:
auto delInvmtL([](Investment* pInvestment) // "L" for
{ // lambda
makeLogEntry(pInvestment); // (stateless)
delete pInvestment;
});
template<class... Ts> // use less
std::unique_ptr<Investment, decltype(delInvmtL)> // memory,
makeInvestment(Ts&&... params); // inlining
// likely
void delInvmtF(Investment* pInvestment) // "F" for
{ // function
makeLogEntry(pInvestment);
delete pInvestment;
}
template<class... Ts> // use more
std::unique_ptr<Investment, decltype(delInvmtF)> // memory,
makeInvestment(Ts&&... params); // inlining
// less
// certain
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 540

Support for Arrays


std::unique_ptr<T[]> has somewhat different interface:
§ Dereferencing and derived-to-base conversions disabled.
§ Indexing support added.
Eliminates object-vs.-array ambiguity:
§ std::unique_ptr<T> points to single object.
§ std::unique_ptr<T[]> points to array.
Very few use cases for std::unique_ptr<T[]>.
§ std::array, std::vector, std::string almost always better.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 541

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Implicit Conversion to std::shared_ptr


Makes using std::unique_ptr practical for factory returns:
§ Leaves ownership policy (exclusive or shared) up to callers:
template<class... Ts> // as before
std::unique_ptr<Investment, decltype(delInvmt)>
makeInvestment(Ts&&... params);

auto pInv1(makeInvestment(args)); // client chooses


// exclusive ownership
std::unique_ptr<Investment,
decltype(delInvmt)>
pInv2{makeInvestment(args)}; // ditto

// client chooses shared ownership


std::shared_ptr<Investment> pInv3{makeInvestment(args)};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 542

Guideline
Use std::unique_ptr for exclusive-ownership resource management.

Based on EMC++ Item 18.


Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 543

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Use std::shared_ptr for Shared-Ownership


Resource Management
Based on reference counting. Implications:
§ std::shared_ptrs twice as big as raw pointers.
std::shared_ptr<T>
T* pointee
RC

§ Memory for reference count (RC) dynamically allocated.


§ RC manipulations use atomic instructions.
Æ Slower than normal machine instructions.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 544

Deleters
Type of deleter not part of the std::shared_ptr type.
§ Important difference from std::unique_ptr:
auto loggingDel([](Widget *pw) // custom deleter
{
makeLogEntry(pw);
delete pw;
});
std::unique_ptr< // deleter type is
Widget, decltype(loggingDel) // part of ptr type
> upw{new Widget, loggingDel};

std::shared_ptr<Widget> // deleter type isn't


spw{new Widget, loggingDel}; // part of ptr type

Using an object whose type is hidden


in this way is called type-erasure.
Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 545

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Deleters
Design nicely flexible:
auto customDeleter1([](Widget *pw) { … }); // different
auto customDeleter2([](Widget *pw) { … }); // deleter types
std::shared_ptr<Widget> pw1{new Widget,
customDeleter1};
std::shared_ptr<Widget> pw2{new Widget,
customDeleter2};
std::vector<std::shared_ptr<Widget>> // container of
vpw{pw1, pw2}; // shared_ptrs
// w/differing
// deleters
Implication:
§ Size of std::shared_ptr unaffected by size of deleter.
Æ Makes std::vector<std::shared_ptr> possible.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 546

Deleters
But deleters may be arbitrarily large:
§ Stateful function objects (e.g., closures with captured state).
If sizeof(std::shared_ptr<T, BigDeleterType>) independent of
sizeof(BigDeleterType),
§ Where store BigDeleterType object for std::shared_ptr<T>?

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 547

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Control Blocks
As part of control block:
§ Object containing RC.
More detailed view of std::shared_ptr objects:

T* T Object

Control Block
RC
Weak Count
Other Data
(e.g.,
deleter,
allocator,
etc.)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 548

Control Block Creation


When are control blocks created?
§ Each call to std::make_shared.
Æ New object is being created, so it can’t yet have a control block.
§ std::shared_ptr created from std::unique_ptr or std::auto_ptr.
Æ No control block should exist for objects managed by exclusive-ownership
smart pointers.
§ std::shared_ptr created from a raw pointer.
Æ If control block for pointee existed, caller would presumably construct the
std::shared_ptr from a smart pointer.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 549

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Control Block Creation


Using a raw pointer for more than one std::shared_ptr ⇒ disaster.
auto pw(new Widget); // pw is raw ptr

std::shared_ptr<Widget> spw1{pw, loggingDel}; // create ctrl
// block for *pw

std::shared_ptr<Widget> spw2{pw, loggingDel}; // create 2nd
// control block
// for *pw!
loggingDel will be called twice.
§ Once when each RC becomes 0.
§ Second call yields UB.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 550

Control Block Creation


To minimize likelihood of this problem,
§ Prefer std::make_unique and std::make_shared to direct use
of new.
When that’s impossible (e.g., custom deleter needed),
§ Create std::shared_ptr from new in standalone statement:
std::shared_ptr<Widget> spw1{new Widget, // create control
loggingDel}; // block
Æ Creation of second std::shared_ptr now likely uses spw1:

std::shared_ptr<Widget> spw2{spw1}; // spw2 shares spw1's


// control block

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 551

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Control Block Creation and this


Consider:
std::vector<std::shared_ptr<Widget>> // data structure for
processedWidgets; // processed Widgets

struct Widget {

void process(); // Widget-processing
… // function
};
void Widget::process()
{
… // process the Widget
processedWidgets.emplace_back(this); // uh oh...
}
This is a problem waiting to happen.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 552

Control Block Creation and this


this is a raw pointer, so:
§ Call to emplace_back creates a control block for *this.
But there may already be std::shared_ptrs pointing to *this.
§ If so, we’re set up for UB.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 553

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Intrusive vs Un-intrusive Design


A library with an un-intrusive design can work with types which are created
independently of the library.
§ std::vector is an example of un-intrusive design
§ std::vector can store types that were created without that intent
§ un-intrusive libraries maximize usability
Intrusive libraries require that the types they work with be designed with that
intent.
§ This requirement “intrudes” on the design of these types.
§ Intrusive designs can have performance / feature advantages
§ Consider a design for a reference counted smart pointer that required that
every object it could point to had a data member for tracking the reference
count—no need for a control block.
§ An intrusive design would solve the processed Widgets problem—a Widget
that knows about the smart pointer pointing to it can return a copy of that
pointer.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 554

Intrusive vs Un-intrusive Design


std::shared_ptr is supports both un-intrusive and intrusive designs.
§ Usually it is un-intrusive
§ Optionally, types can be created with the intent of being shared by it.
§ With the intrusive option, we can solve the processed Widgets problem.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 555

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

std::enable_shared_from_this
Solution is std::enable_shared_from_this.
§ A base class (template).
Æ Uses CRTP (“Curiously Recurring Template Pattern”).
§ Inherit from it to safely convert this to a std::shared_ptr.
§ Call shared_from_this instead of using this.
struct Widget: std::enable_shared_from_this<Widget> {

};
void Widget::process()
{

processedWidgets.emplace_back(shared_from_this());
}

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 556

std::enable_shared_from_this
shared_from_this looks up current control block for *this.
§ If none present, UB.
Æ Implementations typically throw.
Classes using std::enable_shared_from_this often limit creation to factory
functions returning std::shared_ptrs.
§ Class instances have control blocks at birth.
§ Subsequent uses of shared_from_this hence safe.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 557

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

std::enable_shared_from_this
struct Widget: std::enable_shared_from_this<Widget> {
template<class... Ts> // factory function;
static std::shared_ptr<Widget> // calls private ctor
create(Ts&&... params);

void process(); // as before, uses
… // shared_from_this
private:
… // ctors
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 558

Cost Summary
§ std::shared_ptrs twice as big as raw pointers.
§ Memory for control block dynamically allocated.
Æ Typical size: 4 words + sizeof(deleter) + sizeof(allocator).
w 2 words for reference counts.
w 1 word for typed pointer to object.
w 1 word for vptr. (Pointee destruction entails virtual call.)
w Default deleter and allocator should use no memory.
Æ Cost of dynamic allocation avoided when std::make_shared used.
w It becomes part of allocation for pointee.
§ RC manipulations use atomic instructions.
Æ Incurred for copy operations, but not for moves.
§ Dereferencing cost same as for raw pointer.
Costs are reasonable for shared-ownership resource management.
§ When exclusive ownership suffices, std::unique_ptr a better choice.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 559

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

“Smart” Pointee Destruction


Pointee type captured during std::shared_ptr construction.
§ Enables polymorphic deletion without a virtual destructor:
struct Base {
… // no virtual dtor
};
struct Derived: Base { ... };
{
std::shared_ptr<Base> spb{new Derived}; // Base ptr to
... // Derived object
} // spb's RC→0,
// Derived dtor
// called
Æ True feature or just support for suspect programming?

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 560

“Smart” Pointee Destruction


§ Also enables safe deletion through std::shared_ptr<void>.
std::vector<std::shared_ptr<void>> pointers;
Æ When pointers destroyed, all elements’ pointees have correct dtor called.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 561

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

“Smart” Pointee Destruction


Works because Control Block consists of base and derived parts:
§ std::shared_ptr ctor instantiates ControlBlockBase
derived class that knows type of
pointer passed:
template<class T>
struct shared_ptr { ControlBlockImpl<U>
template<class U>
shared_ptr(U* ptr)
: pCtrlBlock{new ControlBlockImpl<U>{ptr}}
{ ... }
...
private:
T* pObject;
ControlBlockBase *pCtrlBlock;
};

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 562

“Smart” Pointee Destruction


§ When RC→0,
Æ ControlBlockBase virtual dtor called.
Æ That resolves to ControlBlockImpl<U> dtor.
Æ It invokes deleter on stored U* pointer.
w Obviates need for virtual destructor in T.
w Also handles case where T is void.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 563

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

“Smart” Pointee Destruction


std::unique_ptr lacks control blocks, hence offers no “smart” pointee
destruction.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 564

Guideline
Use std::shared_ptr for shared-ownership resource management.

Based on EMC++ Item 19.


Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 565

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Prefer std::make_unique and std::make_shared


to Direct use of new
std::shared_ptrs commonly initialized with new expressions:
std::shared_ptr<Widget> pw{new Widget{args}};
Drawbacks:
§ Gauche. Raw use of new?!
§ Inefficient. Overuses heap, wastes space, wastes time.
§ Poor software engineering. Violates DRY principle.
§ Exception-risky. Easy to write exception-unsafe code.
Æ See next slide.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 566

Prefer std::make_unique and std::make_shared


to Direct use of new
One of the arguments for using make_unique and make_shared was that some
code calling new directly was theoretically unsafe because of the freedom that
compilers had in how they ordered operators.
This theoretical exception-safety “gotcha” was closed in C++17, when the
committee added some constraints to the ordering of operations.
Since no existing compiler ever generated code in this unsafe, but theoretically
legal, way, there is no reason to have this concern today.
This does not, however, change the advice of this guideline.

Kalb for NVIDIA Copyrighted material, all rights reserved.


http://cpp.training/ Slide 567

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Multiple Allocations
std::shared_ptr needs heap-based reference count to point to.
§ Created by first std::shared_ptr to object.
Hence
std::shared_ptr<Widget> pw{new Widget{args}};
incurs two heap allocations:
§ One for new Widget. CB
pw
§ One for control block (CB) :Widget
inside std::shared_ptr ctor).

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 568

Reducing Allocations
std::make_shared makes one allocation for both object and CB:
auto pw(std::make_shared<Widget>(args));

pw CB :Widget

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 569

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

A Closer look at std::shared_ptr


Without use of std::make_shared,
std::shared_ptr<Widget> pw{new Widget{args}};
high-level memory layout:

CB
pw
:Widget
In more detail:

Control Block
pw
:Widget

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 570

Still Closer
Sample implementation (Boost 1.52):
shared_ptr<T>
px T Object
pn.pi
RC
Weak Count

Optional { Deleter
Allocator

CB→object pointer needed for implementation edge cases.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 571

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

“We Know Where You Live” Optimization


std::make_shared obviates these cases. So
CB
sp
Object
can be optimized to:

sp CB

Object
CBs hence smaller (and faster to initialize).

Optimization due to Stephan T. Lavavej.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 572

Efficiency Summary
std::shared_ptr<Widget> sp1{new Widget{args}}; // 2 allocs; CB
// has ptr to
// Widget
auto sp2(std::make_shared<Widget>(args)); // 1 alloc; CB
// has no ptr to
// Widget
// (if WKWYL used)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 573

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

std::make_unique (C++14 Only)


Easy to implement if not yet provided by your vendor:
template<class T> struct _Unique_if { // copied out of C++14
typedef unique_ptr<T> _Single_object; // standardization proposal!
};
template<class T> struct _Unique_if<T[]> {
typedef unique_ptr<T[]> _Unknown_bound;
};
template<class T, size_t N> struct _Unique_if<T[N]> {
typedef void _Known_bound;
};
template<class T, class… Args> // single-object version
typename _Unique_if<T>::_Single_object
make_unique(Args&&… args) {
return unique_ptr<T>{new T{std::forward<Args>(args)…}};
}
template<class T> // array version
typename _Unique_if<T>::_Unknown_bound
make_unique(size_t n) {
typedef typename remove_extent<T>::type U;
return unique_ptr<T>{new U[n]{}};
}
template<class T, class... Args>
typename _Unique_if<T>::_Known_bound
make_unique(Args&&...) = delete;

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 574

Staying DRY
Only std::make_shared and std::make_unique adhere to DRY:
§ DRY = “Don’t Repeat Yourself.”
std::shared_ptr<Widget> sp1{new Widget{args}}; // !DRY
auto sp2(std::make_shared<Widget>(args)); // DRY

std::unique_ptr<Widget> up1{new Widget{args}}; // !DRY


auto up2(std::make_unique<Widget>(args)); // DRY
Forms using std::make_shared/std::make_unique also less to type :-)

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 575

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

std::allocate_shared
Like std::make_shared, but permits use of custom allocator:
CustomAllocator ca;

auto pw(std::allocate_shared<Widget>(ca, args));

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 576

Custom Deleters
No custom deleters with std::make_shared/std::allocate_shared.
Widget* makeWidget(params); // factory function
void reclaimWidget(Widget*); // disposal function

std::shared_ptr<Widget> pw1{makeWidget(args),
reclaimWidget}; // custom deleter
auto pw2(
std::make_shared<Widget>(makeWidget(args),
reclaimWidget)); // interpreted as
// Widget ctor arg!
Same restriction applies to std::make_unique.
Need for custom deleter ⇒ can’t use std::make_shared,
std::allocate_shared, std::make_unique.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 577

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Class-Specific Memory Allocators


Generally a bad match for std::make_shared.
struct Widget {
...
void* operator new(std::size_t sz);
void operator delete(void *pMem, std::size_t sz);
...
};

auto pw( // create Widget on


std::make_shared<Widget>( ctor args )); // heap w/o using
// Widget’s operator
// new!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 578

When std::weak_ptrs Outlive std::shared_ptrs


Object memory allocated until all std::weak_ptrs expire.
§ Potentially problematic for large objects.
auto p(std::make_shared<ReallyBigObject>(args));
… // create std::shared_ptrs and
// std::weak_ptrs
… // final std::shared_ptr goes away
… // std::weak_ptrs continue to exist;
// memory for ReallyBigObject
// remains allocated

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 579

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

When std::weak_ptrs Outlive std::shared_ptrs


With std::make_shared:
auto p(std::make_shared<ReallyBigObject>(args));
… // ≥ 1 std::shared_ptrs exist
CB
RC > 0
p WC :ReallyBigObject

… // 0 std::shared_ptrs exist,
// ≥ 1 std::weak_ptrs exist
CB
0
std::weak_ptrs WC > 0

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 580

When std::weak_ptrs Outlive std::shared_ptrs


Without:
std::shared_ptr<ReallyBigObject> p{new ReallyBigObject{args}};
… // ≥ 1 std::shared_ptrs exist
CB
RC > 0
p WC
:ReallyBigObject

… // 0 std::shared_ptrs exist,
// ≥ 1 std::weak_ptrs exist
CB
0
std::weak_ptrs WC > 0

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 581

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guideline
Use std::make_shared and std::make_unique whenever possible.

Based on EMC++ Item 21.


Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 582

Summary: Smart Pointers


§ Use std::unique_ptr for exclusive-ownership resource management.
§ Use std::shared_ptr for shared-ownership resource management.
§ Prefer std::make_unique and std::make_shared to direct use of new.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 583

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Efficiency

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 584

Efficiency Overview
The 80-20 Rule
Efficiency and the language C++:
§ Eliminating unnecessary calls to constructors and destructors
Efficiency and C++’s standard library:
§ Consider Emplacement Instead of Insertion

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 585

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Caveat

More computing sins are committed in the name of efficiency


(without necessarily achieving it) than for any other single
reason — including blind stupidity.
—W. A. Wulf

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 586

Achieving Runtime Efficiency


§ Usually, your goal is fast enough, not as fast as possible
§ Find bottlenecks before fine-tuning:
Æ Remember the 80-20 rule
Æ Programmer intuition is usually wrong
Æ Souped-up computation won’t help an IO-bound program;
a better IO library won’t speed up a CPU-bound program
Æ Use a profiler (on representative data)!

§ Learn to instinctively avoid C++ constructs with poor performance:


Æ Needless calls to constructors and destructors
Æ Unnecessary generation of temporaries
Æ Too many or overly expensive calls to new and delete
Æ Overuse of associative containers
Æ Failure to embrace idiomatic STL constructs

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 587

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Efficiency and the Language C++


§ Minimize implicit calls to constructors and destructors.
Æ As few as possible — but no fewer!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 588

Calls to Constructors and Destructors


Constructors are called:
§ When an object is defined (stack, heap, or static)
§ When an array of objects is defined (stack, heap, or static)
§ When a function parameter is passed by value
§ When a function returns an object
This applies even to compiler-generated temporary objects.

Destructors are called:


§ When a named stack object, array, or parameter goes out of scope
§ When a heap object or array is deleted
§ For static objects, at the end of the program
§ For temporary objects, at the end of the “full expression” in which they
are created

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 589

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Examples
#include <string>
// struct string { // string acts as if it were
// string();
// string(char const*);
// string(string const& rhs);
// ...
// };
using namespace std;
string s1{"Hello”}; // 1 ctor call
string s2{s1}; // 1 ctor call
string s3 = "Hello"; // 1 or 2 ctor calls
string sa1[10]; // 10 ctor calls
string sa2[ ] // 3 or 6 ctor calls
{ string{"One”}, string{"Two”}, string{"Three”} };
Destructors will be called when these objects go out of scope.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 590

Examples
std::string interleave(std::string str1, std::string str2);
std::cout << interleave(s1, "Hello"); // at least 3 ctor calls
Again, destructors will be called when these objects go away.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 591

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Constructors, Destructors,
Inheritance, and Composition
Inheritance results in implicit calls:
§ Base class constructors/destructors are called for derived class objects
So does composition:
§ Data members are initialized via constructors and destroyed via
destructors

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 592

Example
struct Person {
Person(std::string const& who, std::string const& where);
...
private:
std::string name;
std::string address;
};
struct Student: Person {
Student(std::string const& who, std::string const& where);
...
private:
std::string idNumber;
};
std::string name{"Chris”};
std::string location{"Bermuda”};
int main() {
Student s(name, location); // 5 ctors called
} // 5 dtors called
Moral: construction and destruction of objects can be very expensive!

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 593

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guidelines for Avoiding Unnecessary


Constructions and Destructions
1. Pass object parameters by reference-to-const instead of by value:
bool operator==(Widget lhs, Widget rhs); // bad
bool operator==(Widget const& lhs, // good
Widget const& rhs);
Æ This requires the existence of const member functions!
Æ This guideline is especially important when writing templates:

template<class T>
bool operator!=(T const& lhs, T const& rhs) { return !(lhs == rhs); }
Æ Built-in types are an exception; pass-by-value is okay for them:
std::vector<Widget>
makeClones(Widget const& w, // pass by ref
int numClones); // pass by value
Æ Another exception: things passed by value in the STL, e.g., iterators
and function objects

Based on EC++/3E Item 20.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 594

Guidelines for Avoiding Unnecessary


Constructions and Destructions
2. Defer object definitions as long as possible:
Æ Ideallyuntil initialization arguments can be provided
std::string getUserName();
void f() // bad
{
std::string name;
...
name = getUserName();
...
}
void f() // good
{
...
std::string name{getUserName()};
...
}

Based on EC++/3E Item 26.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 595

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guidelines for Avoiding Unnecessary


Constructions and Destructions
3. Prefer initialization to assignment in constructors:
struct NamedData {
NamedData(std::string const& initName, void *dataPtr);
...
private:
std::string name;
void *data;
};
NamedData::NamedData(std::string const& initName, void *dataPtr)
{
name = initName; // bad
data = dataPtr;
}
NamedData::NamedData(std::string const& initName, void *dataPtr)
: name{initName}, data{dataPtr} // good
{}

Based on EC++/3E Item 4.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 596

Guidelines for Avoiding Unnecessary


Constructions and Destructions
4. Consider overloading to avoid implicit type conversions:
bool operator==(std::string const& lhs, // declared in
std::string const& rhs); // <string>
std::string s;
if (s == "Hello") ... // converts "Hello" to string
if ("Hello" == s) ... // via a temporary object
These additional functions avoid the need to generate temporaries:
bool operator==(std::string const& lhs, char const*rhs);
bool operator==(char const *lhs, std::string const& rhs);
The standard library includes all these versions of operator== for
strings.

Based on MEC++ Item 21.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 597

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guidelines for Avoiding Unnecessary


Constructions and Destructions
5. Consider using op= instead of op:
struct Rational {
...
Rational& operator*=(Rational const& rhs);
...
};
Rational operator*(Rational const& lhs, Rational const& rhs);

Rational r1, r2;


r1 = r1 * r2; // bad
r1 *= r2; // good

Based on MEC++ Item 22.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 598

The Return Value Optimization


Consider this class:
struct Rational {
Rational(int num = 0, int denom = 1);
...
friend Rational operator*(Rational const& lhs, Rational const& rhs);
private:
int n;
int d;
};

Note the by-value return from operator*.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 599

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

The Return Value Optimization — Take 1


Consider this implementation:
Rational operator*(Rational const& lhs, Rational const& rhs)
{
return {lhs.n * rhs.n, lhs.d * rhs.d};
}
Technically, this implementation requires two temporaries:
§ One for the return expression
§ One for the function’s return value
But temporaries may be optimized away:
Rational a{3, 5}, b{1, 2};
Rational prod{a * b}; // "a * b" may be
// constructed in prod

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 600

The Return Value Optimization


When functions enabling return-value optimization are declared inline,
even the function call can be optimized away:
inline Rational operator*( Rational const& lhs,
Rational const& rhs)
{
return {lhs.n * rhs.n, lhs.d * rhs.d};
}
Rational a{3, 5}, b{1, 2};
Rational prod{a * b}; // "a * b" can be constructed in prod
// without making a function call

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 601

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

The Return Value Optimization — Take 2


Here is an alternative implementation:
Rational operator*( Rational const& lhs,
Rational const& rhs)
{
Rational result;
result.n = lhs.n * rhs.n;
result.d = lhs.d * rhs.d;
return result;
}
This function is also eligible for the RVO.
Rational prod{a * b}; // "a * b" may still be
// constructed in prod

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 602

Guidelines for Avoiding Unnecessary


Constructions and Destructions
6. Facilitate the return value optimization:
§ Return expressions yielding temporaries of the same type as the
function’s return value
§ Have all return statements in a function return the same local variable
(of the same type as the function’s return value)

Based on MEC++ Item 20.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 603

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guidelines for Avoiding Unnecessary


Constructions and Destructions
7. Consider using reference counting:
Æ Eliminatesunnecessary constructions and destructions of an object’s
data members.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 604

Consider Emplacement Instead of Insertion


Insertion functions take parameters of type (reference to) T:
§ push_front(T)
§ push_back(T)
§ insert(position, T)
§ insert_after(position, T)
E.g., in std::vector<std::string>:
void push_back(std::string const&);
void push_back(std::string&&);

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 605

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Consider Emplacement Instead of Insertion


Emplacement functions take constructor arguments for T:
§ emplace_front(ctor args for T)
§ emplace_back(ctor args for T)
§ emplace(position, ctor args for T)
§ emplace_hint(position, ctor args for T)
§ emplace_after(position, ctor args for T)
For containers<T>:
template<class... Args>
void emplace_back(Args&&...);

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 606

The Case for Emplacement


Avoids temporary creation:
std::vector<std::string> vs;
vs.push_back("xyzzy"); // create temp
// move temp into vector
// destroy temp
vs.emplace_back("xyzzy"); // create string inside vector

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 607

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

The Case for Emplacement


For copyable/movable types, emplacement can do it all!
std::string queenOfDisco{"Donna Summer"};
vs.push_back(queenOfDisco); // copy-construct queenOfDisco
// at end of vs
vs.emplace_back(queenOfDisco); // ditto

Ergo: Prefer emplacement to insertion.


§ Should never be slower, should sometimes be faster.
§ Right?

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 608

Summary
Performance:
§ Emplacement can reduce number of constructions/destructions.
§ In theory, emplacement never costs more than insertion.
§ In practice, emplacement often does.
§ Emplacement most likely a win when:
Æ Value being added is constructed into the container, not assigned.
Æ Argument(s) passed are different from T (for Container<T>).
Æ Value to be added unlikely to be rejected as a duplicate.

Safety:
§ Insertion “respects” explicit constructors, emplacement doesn’t.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 609

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.
Topics From Effective C++ Programming

Guideline
Consider emplacement instead of insertion.

Based on EMC++ Item 42.


Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 610

Summary: Efficiency
§ Use the 80-20 rule to focus your efforts.
§ Avoid unnecessary temporaries.
§ Consider emplacement where it is likely to be more efficient than insertion.

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material, all rights reserved.
http://www.aristeia.com/ http://cpp.training/ Slide 611

Scott Meyers, Software Development Consultant Kalb for NVIDIA Copyrighted material,
http://www.aristeia.com/ http://cpp.training/ all rights reserved.

Você também pode gostar