SlideShare uma empresa Scribd logo
1 de 88
Baixar para ler offline
Effective Modern C++
Kai
Template Type Deduction
• The Type deduction for T is dependent not just on
the type of expr, but also on the form of
ParamType.
• ParamType is a pointer or reference type.
• ParamType is a universal reference.
• ParamType is neither a pointer nor a reference.
template<typename T>
void f(ParamType param);
f(expr)
ParamType is a pointer or reference type
1. If expr’s type is a reference, ignore the reference part.
2. Then pattern-match expr’s type against ParamType to
determine T.
template<typename T>
void f(T& param);
int x = 27;
const int cx = x;
const int& rx = x;
f(x); // T is int, param’s type is int&
f(cx); // T is const int, param’s type is const int&
f(rx); // T is const int, param’s type is const int&
ParamType is a universal reference
• If expr is an lvalue, both T and ParamType are deduced to be lvalue references.
• It’s the only situation in template type deduction where T is deduced to be a
reference.
• ParamType is declared using the syntax for an rvalue reference, its deduced
type is an lvalue reference.
• If expr is an rvalue, the Case 1 rules apply.
template<typename T>
void f(T&& param);
int x = 27;
const int cx = x;
const int& rx = x;
f(x); // T is int&, param’s type is int&
f(cx); // T is const int&, param’s type is const int&
f(rx); // T is const int&, param’s type is const int&
f(27); // T is int, param’s type is int&&
ParamType is neither a pointer nor a reference
template<typename T>
void f(T param);
int x = 27;
const int cx = x;
const int& rx = x;
f(x); // T is int, param’s type is int
f(cx); // T is int, param’s type is int
f(rx); // T is int, param’s type is int
• Param will be a copy of whatever is passed in.
1. If expr’s type is a reference, ignore the reference part.
2. If expr is const, ignore that. If it’s volatile, also ignore that.
Array Arguments
template<typename T>
void f(T param);
// The type of an array passed to a template function by value is deduced
// to be a pointer type.
const char name[] = “J. P. Briggs”;
f(name); // T deduced as const char *
// Arrays decay into pointer.
template<typename T>
void f(T& param); // passed to a template function by reference
f(name); // T deduced to be const char [13]
// Param’s type is const char (&)[13]
Array Arguments
template<typename T, std::size_t N>
constexpr std::size_t arraySize(T (&)[N]) noexcept
{
return N;
}
int keyVals[] = {1, 3, 7, 9, 11, 22, 35};
int mappedVals[arraySize(keyVals)];
std::array<int, arraySize(keyVals)> mappedVals;
• The ability to declare references to array enables creation of a
template that deduces the number of elements that an array
contains.
Function Arguments
void someFunc(int, double); // type is void (int, double)
template<typename T>
void f1(T param); // pass by value
template<typename T>
void f2(T& param); // pass by reference
f1(someFunc); // param’s type is void (*)(int, double)
// Function types decay into function pointers.
f2(someFunc); // param’s type is void (&)(int, double)
auto type deduction
auto x = 27;
const auto cx = x;
const auto& rx = x;
template<typename T>
void x(T param);
x(27)
template<typename T>
void cx(const T param);
cx(x);
template<typename T>
void rx(const T& param);
rx(x);
With only one exception, auto type deduction is template type deduction.
1. The type specifier is a pointer or reference, but not a universal reference.
2. The type specifier is a universal reference.
3. The type specifier is neither a pointer nor a reference.
auto type deduction
auto x1 = 27; // int
auto x2(27); // int
auto x3 = {27}; // std::initializer_list<int>
auto x4{27}; // std::initializer_list<int>
When the initialiser for an auto-declared variable is enclosed in braces,
the deduced type is a std::initializer_lists.
auto x = {11, 23, 9};
template<typename T>
void x(T param);
x({11, 23, 9}); // error! can’t deduce type
auto assumes that a braced initialiser represents a std::initializer_list,
but template type deduction doesn’t.
template<typename T>
void x(std::initializer_list<T> param);
x({11, 23, 9}); // T deduced as int, param’s type is std::initializer_list<int>
auto type deduction
C++14:
1. permit auto to indicate that a function’s return type should be deduced.
2. lambdas may use auto in parameter declarations.
these use of auto employ template type deduction, not auto type deduction.
auto createInitList()
{
return {1, 2, 3}; // error: can’t deduce type for {1, 2, 3}

}
• Given a name or an expression, decltype tells you the name’s or the
expression’s type.
• The primary use for decltype is declaring function templates where the
function’s return type depends on its parameter types.
decltype
// C++11
template<typename Container, typename Index>
auto autoAndAccess(Container& c, Index i)
-> decltype(c[i])
{
authenticateUser();
return c[i];

}
// C++14
template<typename Container, typename Index>
auto autoAndAccess(Container& c, Index i)
{
authenticateUser();
return c[i]; // return type deduced from c[i]

}
• To prevent to remove reference-ness of an initialising expression.

(Rule 3 in template type deduction.)
decltype
// C++14
template<typename Container, typename Index>
decltype(auto) autoAndAccess(Container& c, Index i)
{
authenticateUser();
return c[i]; // return type deduced from c[i]

}
std::deque<int> d;
authAndAccess(d, 5); // d[5] returns an int&
decltype
// C++14
Widget w;
const Widget& cw = w;
auto myWidget1 = cw; // myWidget1’s type is Widget
// const-ness and reference-ness will be removed
decltype(auto) myWidget2 = cw; // myWidget2’s type is const Widget&
decltype
// Accept left-reference and right-reference container
// C++11
template<typename Container, typename Index>
auto
authAndAccess(Container&& c, Index i)
-> decltype(std::forward<Container>(c)[i])
{
authenticateUser();
return std::forward<Container>(c)[i];
}
// C++14
template<typename Container, typename Index>
decltype(auto)
authAndAccess(Container&& c, Index i)
{
authenticateUser();
return std::forward<Container>(c)[i];
}
decltype
• Applying decltype to a name yields the declared type for that name.
• For lvalue expressions more complicated than names, decltype 

ensures that the type reported is always an lvalue reference.
// C++14
decltype(auto) f1()
{
int x = 0;
…
return x; // decltype(x) is int, so f1 returns int
}
decltype(auto) f2()
{
int x = 0;
…
return (x); // decltype((x)) is int&, so f2 returns int&
// return a reference to a local variable! (undefined behavior)
}
auto - must be initialised
• auto variables have their type deduced from their initialiser, so they

must be initialised.
int x1; // potentially uninitialised
auto x2; // error! initialiser required
auto x3 = 0; // fine, x’s value is well-defined
auto - avoid verbose
template<typename It>
void dwim(It b, It e)
{
while (b != e) {
typename std::iterator_traits<It>::value_type
currValue = *b;
…
}
}
template<typename It>
void dwim(It b, It e)
{
while (b != e) {
auto currValue = *b;
…
}
}
• avoid verbose variable declarations
auto - hold a closure
• std::function is a template in the C++11 Standard Library that

generalised the idea of a function pointer.
• Using std::function is not the same as using auto.
• An auto-declared variable holding a closure has the same type as

the closure, it uses only as much memory as the closure requires.
std::function<bool(const std::unique_ptr<Widget>&,
const std::unique_ptr<Widget>&)>
derefUPLess = [](const std::unique_ptr<Widget>& p1,
const std::unique_ptr<Widget>& p2)
{ return *p1 < *p2; };
// C++11
auto derefUPLess = [](const std::unique_ptr<Widget>& p1,
const std::unique_ptr<Widget>& p2)
{ return *p1 < *p2; };
// C++14
auto derefUPLess = [](const auto& p1,
const auto& p2)
{ return *p1 < *p2; };
auto
• The type of a std::function-declared variable holding a closure is an

instantiation of the std::function template, and that has a fixed size

for any given signature.
• The std::function approach is generally bigger and slower than the

auto approach, and it may yield out-of-memory exceptions.
std::function<bool(const std::unique_ptr<Widget>&,
const std::unique_ptr<Widget>&)>
derefUPLess = [](const std::unique_ptr<Widget>& p1,
const std::unique_ptr<Widget>& p2)
{ return *p1 < *p2; };
// C++11
auto derefUPLess = [](const std::unique_ptr<Widget>& p1,
const std::unique_ptr<Widget>& p2)
{ return *p1 < *p2; };
// C++14
auto derefUPLess = [](const auto& p1,
const auto& p2)
{ return *p1 < *p2; };
auto
• Use auto to avoid potential bugs and performance issues.
std::vector<int> v;
unsigned sz = v.size(); // potential bugs. unsigned is 32-bits on 64-bits machine
// v.size()’s type is std::vector<int>::size_type
auto sz = v.size(); // sz’s type is std::vector<int>::size_type
std::unordered_map<std::string, int> m;
// Key value is const, so actual type is std::pair<const std::string, int>
// There will be a temporary variable of std::pair<std::string, int>
// Every loop has an extra constructor and destructor
for (const std::pair<std::string, int>& p : m) {
…
}
// avoid temporary variable
// simply bind the reference p to each element in m
for (const auto& p : m) {
…
}
auto
• operator[] for std::vector<bool> doesn’t return a reference to

an element of the container. C++ forbids references to bits.
• It returns an object of type std::vector<bool>::reference
std::vector<bool> features(const Widget& w);
Widget w;
auto highPriority = features(w)[5]; // deduced type is std::vector<bool>::reference
// highPriority is a reference to a temporary

// variable. It is a dangling pointer.
processWidget(w, highPriority); // undefined behaviour!
std::vector<bool> features(const Widget& w);
Widget w;
bool highPriority = features(w)[5]; // std::vector<bool>::reference implicit

// convert to bool
processWidget(w, highPriority);
auto
• std::vector<bool>::reference is a proxy class that exists for the

purpose of emulating and augmenting the behaviour of some other type.
• As a general rule, proxy classes don’t play well with auto.
• Objects of such classes are often not designed to live longer than a

single statement.
// solution
std::vector<bool> features(const Widget& w);
Widget w;
auto highPriority = static_cast<bool>(features(w)[5]); // explicitly typed
processWidget(w, highPriority);
// avoid this
auto someVar = expression of proxy class type
Brace Initialiser
• There are three methods to initialise variables.
int x(0); // initialiser is in parentheses
int y = 0; // initialiser follows “=“
int z{ 0 }; // initialiser is in braces
• There were some situations where c++98 had no way to express a 

desired initialisation.
std::vector<int> v{1, 3, 5}; // (C++11) v’s initial content is 1, 3, 5
Brace Initialiser
• Brace initialiser is universal initialiser.
// C++11. Braces can be used to initialise non-static data members.
class Widget {
…
private:
int x{0}; // fine, x’s default value is 0
int y = 0; // also fine
int z(0); // error!
};
// uncopyable objects may be initialised using braces or parentheses.
std::atomic<int> ai1{0}; // fine
std::atomic<int> ai2(0); // fine
std::atomic<int> ai3 = 0; // error!
Brace Initialiser
• Brace initialiser prohibits implicit narrowing conversions.
double x, y, z;
int sum1{x + y + z}; // error! sum of doubles may not be expressible as int
int sum2(x + y + z); // okay
int sum3 = x + y + z; // okay
Brace Initialiser
• std::initializer_list overshadows other constructors to the point

where the other overloads may hardly be considered.
class Widget {
public:
Widget(int i, bool b);
Widget(int i, double d);
Widget(std::initializer_list<long double> il);
…
};
Widget w1(10, true); // call first ctor
Widget w2{10, true}; // call std::initializer_list ctor
Widget w3(10, 5.0); // call second ctor
Widget w4{10, 5.0}; // call std::initializer_list ctor
Brace Initialiser
• Most developers end up choosing one kind of delimiter as a default, using

the other only when they have to.
nullptr
• 0 and NULL are integral type, not pointer type.
• nullptr’s type is std::nullptr_t.

The type implicitly converts to all raw pointer types.
void f(int);
void f(bool);
void f(void*);
f(0); // call f(int), not f(void*)
f(NULL); // might not compile, but typically calls f(int)
nullptr
• improve code clarity
auto result = findRecord();
if (result == 0) { // the return value may be int or pointer
…
}
auto result = findRecord();
if (result == nullptr) { // the return value must be pointer, it is more clear.
…

}
nullptr
• template type deduction
template<typename FuncType,
typename MuxType,
typename PtrType>
auto lockAndCall(FuncType func, MuxType mutex, PtrType ptr) -> decltype(func(ptr))
{
using MuxGuard = std::lock_guard<MuxType>;
MuxGuard g(mutex); // lock mutex
return func(ptr); // call function

} // unlock mutex
int f1(std::shared_ptr<Widget> spw); // call these only when
double f2(std::unique_ptr<Widget> upw); // the appropriate
bool f3(Widget* pw); // mutex is locked
std::mutex f1m, f2m, f3m;
auto result1 = lockAndCall(f1, f1m, 0); // error! ptr’s type is deduced to int
auto result2 = lockAndCall(f2, f2m, NULL); // error!
auto result3 = lockAndCall(f3, f3m, nullptr); // fine. ptr’s type is std::nullptr_t

// implicitly convert to Widget*
Alias Declarations
// use typedef
typedef
std::unique_ptr<std::unordered_map<std::string, std::string>>
UPtrMapSS;
typedef void (*FP)(int, const std::string&);
// use alias declaration
using UPtrMapSS =
std::unique_ptr<std::unordered_map<std::string, std::string>>
using FP = void (*)(int, const std::string&);
Alias Declarations
// alias declaration
template<typename T>
using MyAllocList = std::list<T, MyAlloc<T>>;
MyAllocList<Widget> lw;
// typedef
template<typename T>
struct MyAllocList {
typedef std::list<T, MyAlloc<T>> type;
};
MyAllocList<Widget>::type lw;
• Alias declarations may be templatized, while typedefs cannot.
Alias Declarations
template<typename T>
struct MyAllocList {
typedef std::list<T, MyAlloc<T>> type;
};
template<typename T>
class Widget {
private:
typename MyAllocList<T>::type list; // MyAllocList<T> may be a specialization.
// “type” may be a data member of it
// The names of dependent types must
// be preceded by typename
…
};
• typedef inside a template class
Scoped enum
enum Color { black, white, red }; // unscoped enum
auto white = false; // error! white already declared in this scope
enum class Color { black, white, red }; // scoped enum. avoid name pollution
auto white = false; // fine.
• The name of unscoped enum belong to the scope containing the enum.
Scoped enum
enum class Color { black, white, red }; // scoped enum
std::vector<std::size_t>
primeFactors(std::size_t x);
Color c = Color::white; // fine.
if (c < 14.5) { // error! can’t convert to double
auto factors = primeFactors(c); // error! can’t convert to std::size_t
…
}
• Scoped enum is strongly typed
Scoped enum
enum class Color; // fine.
enum Color; // error!
• Scoped enum could be forward-declared
• If unscoped enum wants to be forward-declared, it needs to specify

underlying type.
enum Color: std::uint8_t; // forward-declaration for unscoped enum
deleted function
// C++98
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:
…
private;
basic_ios(const basic_ios& ); // not defined (link-time error)
basic_ios& operator=(const basic_ios& ); // not defined
}
• prevent calling some particular functions
// C++11
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public: // C++ checks accessibility before deleted status.
// Let compiler show more clear messages.
…
basic_ios(const basic_ios& ) = delete; // if used, it’s a compile-time
basic_ios& operator=(const basic_ios& ) = delete; // error.
…
}
deleted function
template<typename T>
void processPointer(T* ptr);
template<>
void processPointer<void>(void*) = delete;
template<>
void processPointer<char>(char*) = delete;
• prevent use of template instantiations that should be disabled
// template member function
class Widget {
public:
template<typename T>
void processPointer(T* ptr) { … }
};
template<>
void Widget::processPointer<void>(void*) = delete;
override
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
};
class Derived : public Base {
public:
virtual void mf1() const override;
virtual void mf2(int x) override;
virtual void mf3() & override;
};
• make explicit that a derived class function is supposed to override

a base class version
Prefer const_iterators to iterators
std::vector<int> values
auto it = std::find(values.cbegin(), values.cend(), 1983);
values.insert(it, 1998);
• The standard practice of using const whenever possible dictates that you

should use const_iterators any time you need an iterator.
• There is no portable conversion from a const_iterator to an iterator,

not even with a static_cast.
constexpr objects
int sz;
constexpr auto arraySize1 = sz; // error! sz’s value not known at compilation
std::array<int, sz> data1; // error!
constexpr auto arraySize2 = 10;
std::array<int, arraySize2> data2; // fine. arraySize2 is constexpr
int sz2;
const auto arraySize = sz2; // fine. arraySize is const copy of sz2
std::array<int, arraySize> data; // error! arraySize’s value not known at compilation
• constexpr indicates a value that’s not only constant, it’s known

during compilation.
• constexpr objects are, in fact, const, and they do have values that are

known at compile time.
constexpr functions
constexpr
int pow(int base, int exp) noexcept
{
…
}
constexpr auto numConds = 5;
std::array<int, pow(3, numConds)> results; // call pow function at compile-time
auto base = readFromDB(“base”);
auto exp = readFromDB(“exponent”);
auto baseToExp = pow(base, exp); // call pow function at runtime
• constexpr functions produce compile-time constants when they are called

with compile-time constants. If they’re called with values not known until

runtime, they produce runtime values.
constexpr
// C++11
constexpr int pow(int base, int exp) noexcept
{
return (exp == 0 ? 1 : base * pow(base, exp - 1));
}
// C++14
constexpr int pow(int base, int exp) noexcept
{
auto result = 1;
for (int i = 0; i < exp; i++)
result *= base;
return result;

}
• In C++11, constexpr functions may contain no more than a single

executable statement: a return.
constexpr
class Point {
public:
constexpr Point(double xVal = 0, double yVal = 0) noexcept
: x(xVal), y(yVal)
{}
constexpr double xValue() const noexcept { return x; }
constexpr double yValue() const noexcept { return y; }
void setX(double newX) noexcept { x = newX; } // setX could be constexpr in C++14
void setY(double newY) noexcept { y = newY; }
private:
double x, y;
};
constexpr Point p1(9.4, 27.7); // “runs” constexpr actor during compilation
constexpr Point p2(28.8, 5.3);
constexpr Point midpoint(const Point& p1, const Point& p2) noexcept
{
return { (p1.xValue() + p2.xValue()) / 2, (p1.yValue() + p2.yValue()) / 2};
}
constexpr auto mid = midpoint(p1, p2);
• constructors and other member functions may be constexpr
make const member functions thread safe
• roots doesn’t change Polynomial object on which it operates, but as

part of its caching activity, it may need to modify rootVals and

rootsAreValid.
• roots is declared const, but it’s not thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const
{
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
mutable bool rootsAreValid{ false };
mutable RootsType rootVals{};

};
make const member functions thread safe
• std::mutex is a move-only type. A side effect of adding m to Polynomial

is that Polynomial loses the ability to be copied.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const
{
std::lock_guard<std::mutex> g(m);
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
mutable std::mutex m;
mutable bool rootsAreValid{ false };
mutable RootsType rootVals{};

};
make const member functions thread safe
• std::atomic variables are often less expensive than mutex.
• For a single variable or memory location requiring synchronization, use

of a std::atomic is adequate.
class Point {
public:
double distanceFromOrigin() const noexcept
{
++callCount;
return std::sqrt((x * x) + (y * y));
};
private:
mutable std::atomic<unsigned> callCount{ 0 };
double x, y;
};
special member function generation
• the default constructor
• the destructor
• the copy constructor
• the copy assignment operator
• the move constructor (C++11)
• the move assignment operator (C++11)
class Widget {
public:
Widget(Widget&& rhs); // move constructor
widget& operator=(Widget&& rhs); // move assignment operator
};
special member function generation
• the move constructor (C++11)
• the move assignment operator (C++11)
• perform “memberwise moves” on the non-static data members
• “memberwise moves” are more like member wise move requests.
• memberwise move consists of move operations on data members

and base classes that support move operations, but a copy operation

for those that don’t.
• the two move operations are not independent.
• declaring a move constructor prevents a move assignment operator

from being generated.
• declaring a move assignment operator prevents compilers from

generating a move constructor.
• move operations won’t be generated for any class that explicitly declares

a copy operation.
• C++11 does not generate move operations for a class with a user-declared

destructor.
• The Rule of Three: if you declare any of a copy constructor, copy

assignment operator, or destructor, you should declare all three.
special member function generation
• So move operations are generated for classes only if these three things are

true:
• No copy operations are declared in the class.
• No move operations are declared in the class.
• No destructor is declared in the class.
std::unique_ptr
• std::unique_ptr embodies exclusive ownership semantic.
• Copying a std::unique_ptr isn’t allowed.
• By default, that destruction would take place via delete, but, during

construction, std::unique_ptr objects can be configured to use custom

deleters.
auto delInvmt = [](Investment* pInvestment) // accept a raw pointer as argument
{
makeLogEntry(pInvestment);
delete pInvestment;
};
template<typename… Ts>
std::unique_ptr<Investment, decltype(delInvmt)> // specified in unique_ptr
makeInvestment(Ts&&… params)
{
std::unique_ptr<Investment, decltype(delInvmt)> pInv(nullptr, delInvmt);
if ( /* a Stock object should be created */ )
{
pInv.reset(new Stock(std::forward<Ts>(params)…);
}
return pInv;
}
std::unique_ptr
• Deleters that are function pointers generally cause the size of a

std::unique_ptr to grow from one word to two.
• For deleters that are function objects, the change in size depends on

how much state is stored in the function object.
• Stateless function objects (lambda expressions) incur no size penalty.
std::unique_ptr
• std::unique_ptr comes in two forms, one for individual objects )

std::unique_ptr<T>) and one for arrays (std::unique_ptr<T[]>).
• One of its most attractive features is that it easily and efficiently converts to

a std::shared_ptr.
• std::shared_ptr can’t work with arrays.
std::shared_ptr
• When the last std::shared_ptr pointing to an object stops pointing there,

that std::shared_ptr destroys the object it points to.
• A std::shared_ptr can tell whether it’s the last one pointing to a resource

by consulting the resource’s reference count.
• std::shared_ptr are twice the size of a raw pointer.
• Memory for the reference count must be dynamically allocated.
• Increments and decrements of the reference count must be atomic.
std::shared_ptr Custom Deleters
• For std::unique_ptr, the type of the deleter is part of the type of the smart

pointer. For std::shared_ptr, it’s not.
• Specifying a custom deleter doesn’t change the size of a std::shared_ptr

object.
auto loggingDel = [](Widget* pw)
{
makeLogEntry(pw);
delete pw;
};
std::unique_ptr<Widget, decltype(loggingDel)> upw(new Widget, loggingDel);
std::shared_ptr<Widget> spw(new Widget, loggingDel);
pointer to T
pointer to Contrl Block
std::shared_ptr<T>
T Object
Reference Count
Weak Count
Custom Deleter
std::shared_ptr Control Block
• An object’s control block is set up by the function creating the first

std::shared_ptr to the object.
• std::make_shared always creates a control block.
• A control block is created when a std::shared_ptr is constructed from

a unique-ownership pointer.
• When a std::shared_ptr constructor is called with a raw pointer, it

creates a control block.
auto pw = new Widget; // The creation of the raw pointer is bad.
std::shared_ptr<Widget> spw1(pw, loggingDel); // create control block
std::shared_ptr<Widget> spw2(pw, loggingDel); // create 2nd control block
// It will cause undefined behavior.
std::shared_ptr<Widget> spw1(new Widget, loggingDel); // direct use of new
std::shared_ptr<Widget> spw2(spw1); // spw2 uses same control block as spw1
std::shared_ptr
std::vector<std::shared_ptr<Widget>> processedWidgets;
class Widget {
public:
void process();
…
};
void Widget::process()
{
processWidgets.emplace_back(this); // pass raw pointer to shared_ptr
// This is wrong!
// It may cause undefined behaviour.

}
std::shared_ptr
std::vector<std::shared_ptr<Widget>> processedWidgets;
// The Curiously Recurring Template Pattern (CRTP)
class Widget : public std::enable_shared_from_this<Widget> {
public:
// To prevent clients from calling member functions that invoke shared_from_this
// before a std::shared_ptr points to the object.
template<typename… Ts>
static std::shared_ptr<Widget> create(Ts&&… params); // factory method
…
void process();
…
private:
… // private ctors
};
void Widget::process()
{
processWidgets.emplace_back(shared_from_this()); // There must be an existing
// std::shared_ptr that points
// to the current object.

}
std::weak_ptr
• A pointer like std::shared_ptr that doesn’t affect an object’s

reference count.
• std::weak_ptr isn’t a standalone smart pointer. It’s an augmentation of

std::shared_ptr.
auto spw = std::make_shared<Widget>();
std::weak_ptr<Widget> wpw(spw); // wpw points to same Widget as spw.
spw = nullptr; // wpw now dangles.
if (wpw.expired()) … // if wpw doesn’t point to an object…
// “check then create” should be atomic.
// creating a shared_ptr from the std::weak_ptr
// 1. use weak_ptr::lock
std::shared_ptr<Widget> spw1 = wpw.lock(); // if wpw’s expired, spw1 is null
auto spw2 = wpw.lock(); // same as above, but uses auto
// 2. use constructor
std::shared_ptr<Widget> spw3(wpw); // if wpw’s expired, throw std::bad_weak_ptr
std::weak_ptr
• Caching
• Observer design pattern
• keep a list of observers
• Backward pointer
// caching example
std::unique_ptr<const Widget> loadWidget(WidgetID id); // costly version
std::shared_ptr<const Widget> fastLoadWidget(WidgetID id) // caching version
{
static std::unordered_map<WidgetID, std::weak_ptr<const Widget>> cache;
auto objPtr = cache[id].lock();
if (!objPtr) {
objPtr = loadWidget(id); // load the object
cache[id] = objPtr; // cache it
}
return objPtr;

}
A B C
std::shared_ptr std::shared_ptr
std::weak_ptr
std::make_unique && std::make_shared
• std::make_shared is part of C++11.
• std::make_unique is part of C++14.
• This form of the function doesn’t support arrays or custom deleters.
• std::allocate_shared acts like std::make_shared, except

its first argument is an allocator object to be used for the dynamic

memory allocation.
// make_unique implementation for C++11
template<typename T, typename… Ts>
std::unique_ptr<T> make_unique(Ts&&… params)
{
return std::unique_ptr<T>(new T(std::forward<Ts>(params)…));

}
auto upw1(std::make_unique<Widget>()); // with make function
std::unique_ptr<Widget> upw2(new Widget); // without make function
auto spw1(std::make_shared<Widget>()); // with make function
std::shared_ptr<Widget> spw2(new Widget); // without make function
std::make_unique && std::make_shared
int computePriority();
// Before calling processWidget,
// * The expression “new Widget” must be evaluated
// * The constructor for the std::shared_ptr<Widget>
// * computePriority must run
processWidget(std::shared_ptr<Widget>(new Widget), computePriority());
// If compiler emits code as follow
// 1. Perform “new Widget”
// 2. Execute computePriority
// 3. Run std::shared_ptr constructor
// If (2) produces an exception, the dynamically allocated memory will be leaked.
// std::make_shared has no such problems.
processWidget(std::make_shared<Widget>(), computePriority());
std::make_unique && std::make_shared
// Two allocations.
// 1. allocate memory for Widget
// 2. allocate memory for control block of std::shared_ptr
std::shared_ptr<Widget> spw(new Widget);
// One allocation suffices.
// Caveat: Widget memory will not free until no one uses control block.
auto spw = std::make_shared<Widget>();
std::make_unique && std::make_shared
• None of the make functions permit the specification of custom deleters.
• Within the make functions, the perfect forwarding code uses parentheses,

not braces.
• Using make functions to create objects of types with class-specific versions

of operator new and operator delete is typically a poor idea.
std::make_unique && std::make_shared
• Exception safe code without make functions.
void processWidget(std::shared_ptr<Widget> spw, int priority);
void cusDel(Widget *ptr); // custom deleter
// Put allocation of the Widget and the construction of the std::shared_ptr
// into one statement.
std::shared_ptr<Widget> spw(new Widget, cusDel); // exception safe
processWidget(std::move(spw), computePriority()); // use move() to turn lvalue
// to rvalue. It is more
// efficient.
// Because processWidget’s first parameter is passed by value, construction
// from an rvalue entails only a move, while construction from an lvalue requires
// a copy.
Pimpl Idiom
// Original class
class Widget {
public:
Widget();
…
private:
std::string name;
std::vector<double> data;
Gadget g1, g2, g3;
};
// Pimpl Idiom
class Widget {
public:
Widget();
~Widget(); // door is needed
private:
struct Impl; // declare implementation struct
Impl *pImpl; // and pointer to it
};
Pimpl Idiom (raw pointer)
#include “widget.h” // in impl. file “widget.cpp”
#include “gadget.h”
#include <string>
#include <vector>
struct Widget::Impl {
std::string name;
std::vector<double> data;
Gadget g1, g2, g3;
};
Widget::Widget()
: pImpl (new Impl) {}
Widget::~Widget()
{ delete pImpl; }
Pimpl Idiom (std::unique_ptr)
class Widget { // in header “widget.h”
public:
Widget();
~Widget(); // declaration only
Widget(Widget&& rhs); // declaration only
Widget& operator=(Widget&& rhs);
Widget(const Widget& rhs); // declaration only
Widget& operator=(const Widget& rhs);
private:
struct Impl; // incomplete type declaration
std::unique_ptr<Impl> pImpl;
};
Pimpl Idiom (std::unique_ptr)
#include “widget.h” // in impl. file “widget.cpp”
#include “gadget.h”
#include <string>
#include <vector>
struct Widget::Impl {
std::string name;
std::vector<double> data;
Gadget g1, g2, g3;
};
Widget::Widget()
: pImpl(std::make_unique<Impl>()) {}
// dtor use static_assert to ensure that the
// raw pointer is complete type
Widget::~Widget() = default; // destructor needs to see complete type
// generate code to destroy pImpl when exception arises
// (need complete type)
Widget::Widget(Widget&& rhs) = default;
// need to destroy the object pointed to by pImpl
// before reassigning it. (need complete type)
Widget& Widget::operator=(Widget&& rhs) = default;
Widget::Widget(const Widget& rhs) // deep copy
: pImpl(std::make_unique<Impl>(*rhs.pImpl)) {}
Widget& Widget::operator=(const Widget& rhs)
{
*pImpl = *rhs.pImpl;
return *this;

}
std::move
// C++11
template<typename T>
typename remove_reference<T>::type&& // ensuring that && is applied to a type that isn’t a reference.
move(T&& param)
{
using ReturnType =
typename remove_reference<T>::type&&;
return static_cast<ReturnType>(param);
}
// C++14
template<typename T>
decltype(auto) move(T&& param)
{
using ReturnType = remove_reference_t<T>&&;
return static_cast<ReturnType>(param);

}
• std::move does nothing but cast its argument to an rvalue.
std::move
class Annotation {
public:
explicit Annotation(const std::string text)
: value(std::move(text)) // move will convert to const std::string&&
{ … } // It will call copy ctor, not move ctor.
…
private:
std::string value;
};
class string {
public:
…
string(const string& rhs); // copy ctor
string(string&& rhs); // move ctor
};
• Don’t declare objects const if you want to be able to move from them.
• std::move doesn’t guarantee that the object it’s casting will be eligible

to be moved.
std::forward
void process(const Widget& lvalArg);
void process(Widget&& rvalArg);
template<typename T>
void logAndProcess(T&& param) // universal reference
{
auto now = std::chrono::system_clock::now();
makeLogEntry(“Calling ‘process’”, now);
process(std::forward<T>(param)); // T will convey rvalue or lvalue information.
}
Widget w;
logAndProcess(w); // call with lvalue
logAndProcess(std::move(w)); // call with rvalue
• std::forward casts to an rvalue only if its argument was initialised with

an rvalue.
rvalue reference and universal reference
void f(Widget&& param); // rvalue reference
Widget&& var1 = Widget(); // rvalue reference
auto&& var2 = var1; // universal reference (auto declaration)
template<typename T>
void f(std::vector<T>&& param); // rvalue reference
template<typename T>
void f(T&& param); // universal reference (function template parameter)
template<typename T>
void f(const T&& param); // rvalue reference
• Universal references arise in two context.
• function template parameters (It must be precisely “T&&”.)
• auto declarations
• What these contexts have in common is the presence of type deduction.
rvalue reference and universal reference
template<class T, class Allocator = allocator<T>>
class vector {
public:
void push_back(T&& x); // rvalue reference
template <class… Args>
void emplace_back(Args&&… args); // universal reference
…
};
auto timeFuncInvocation =
[](auto&& func, auto&&… params) // universal reference
{
// start timer;
std::forward<decltype(func)>(func)(
std::forward<decltype(params)>(params)…);
// stop timer and record elapsed time;
};
Use std::forward on universal reference
// Example 1
class Widget {
public:
template<typename T>
void setName(T&& newName) // universal reference
{ name = std::move(newName); } // bad idea to use move() on universal reference
…
private:
std::string name;
};
std::string getWidgetName();
Widget w;
auto n = getWidgetName();
w.setName(n); // move n into w
// n’s value now unknown (potential error!)
// Example 2
template<typename T>
Fraction
reduceAndCopy(T&& frac)
{
frac.reduce();
return std::forward<T>(frac); // move rvalue into return value, copy lvalue

}
Use std::move on rvalue reference
Matrix
operator+(Matrix&& lhs, const Matrix& rhs)
{
lhs += rhs;
return std::move(lhs); // move lhs into return value (more efficient)
}
Matrix
operator+(Matrix&& lhs, const Matrix& rhs)
{
lhs += rhs;
return lhs; // copy lhs into return value

}
Widget makeWidget()
{
Widget w;
…
return std::move(w); // DO NOT apply move() on local variable.
// It will hinder RVO.
// If the conditions for the RVO are met, but
// compiler choose not to perform copy elision,
// the object being returned must be treated as
// an rvalue. (std::move is implicitly applied.)
}
Avoid overloading on universal references
template<typename T>
void logAndAdd(T&& name) // (1)
{
auto now = std::chrono::system_clock::now();
log(now, “logAndAdd”);
names.emplace(std::forward<T>(name));

}
void logAndAdd(int idx) // (2)
{
auto now = std::chrono::system_clock::now();
log(now, “logAndAdd”);
names.emplace(nameFromIdx(idx));
}
short nameIdx;
logAndAdd(nameIdx); // Error! It will be resolved to logAndAdd(short&& name). Match (1).
// Exactly match (1).
// Promotion to match (2). Function (1) is better than (2).
• Functions taking universal references are the greediest functions in C++.

They instantiate to create matches for almost any type of argument.
Avoid overloading on universal references
class Person {
public:
template<typename T>
explicit Person(T&& n) // (1)
: name(std::forward<T>(n)) { … }
explicit Person(int idx) // (2)
: name(nameFromIdx(idx)) { … }
Person(const Person& rhs); // (3) copy ctor (compiler-generated)
Person(Person&& rhs); // (4) move ctor (compiler-generated)
…
private:
std::string name;
};
Person p(“Nancy”);
auto cloneOfP(p); // Error! It will be resolved to Person(Person&). Match (1).
const Person cp(“Nancy”);
auto cloneOfP(cp); // Calls copy ctor. Match (1) and (3).
// Normal function is preferred.
Avoid overloading on universal references
class Person {
public:
template<typename T>
explicit Person(T&& n) // (1)
: name(std::forward<T>(n)) { … }
explicit Person(int idx) // (2)
: name(nameFromIdx(idx)) { … }
Person(const Person& rhs); // (3) copy ctor (compiler-generated)
Person(Person&& rhs); // (4) move ctor (compiler-generated)
…
private:
std::string name;
};
class SpecialPerson : public Person {
pubic:
SpecialPerson(const SpecialPerson& rhs) // copy ctor
: Person(rhs) { … } // Error. Calls (1), not base class copy ctor (3)
SpecialPerson(SpecialPerson&& rhs) // move ctor
: Person(std::move(rhs)) { … } // Error. Calls (1), not base class move ctor (4)
};
Tag Dispatch
template<typename T>
void logAndAdd(T&& name)
{
logAndAddImpl(
std::forward<T>(name),
std::is_integral<typename std::remove_reference<T>::type>() // remove reference before
// pass to is_integral<T>()
);
}
template<typename T>
void logAndAddImpl(T&& name, std::false_type)
{
auto now = std::chrono::system_clock::now();
log(now, “logAndAdd”);
names.emplace(std::forward<T>(name));
}
void logAndAddImpl(int idx, std::true_type)
{
logAndAdd(nameFromIdx(idx));

}
std::enable_if
class Person {
public:
// syntax: enable_if<condition>::type
// is_base_of<T1, T2>::value is true if T2 is derived from T1
// std::decay<T>::type removes reference and cv-qualifiers(i.e., const or volatile) from T
template<
typename T,
typename = typename std::enable_if<
!std::is_base_of<Person,
typename std::decay<T>::type
>::value
&&
!std::is_integral<typename std::remove_reference<T>::type
>::value
>::type
>
explicit Person(T&& n); // universal reference constructor
…

};
• std::enable_if gives you a way to force compilers to behave as if a

particular template didn’t exist.
Reference collapsing
When a lvalue is passed as an argument, T is deduced to be an lvalue reference.
When an rvalue is passed, T is deduced to be a non-reference.
template<typename T>
void func(T&& param);
Widget widgetFactory(); // function returning rvalue
Widget w; // a variable (an lvalue)
func(w); // T deduced to be Widget&.
// func(Widget& && param) => func(Widget& param)
func(widgetFactory()); // T deduced to be Widget
• Reference to reference is illegal.
• Compiler may produce reference to reference in particular contexts.
• If either reference is an lvalue reference, the result is an lvalue reference.

Otherwise the result is an rvalue reference.
• A universal reference isn’t a new kind of reference, it’s actually an rvalue

reference in a context where two conditions are satisfied:
• Type deduction distinguishes lvalues from rvalues. Lvalues of type T

are deduced to have type T&, while rvalues of type T yield T as their

deduced type.
• Reference collapsing occurs.
Perfect forwarding failure cases
template<typename T>
void fwd(T&& param)
{
f(std::forward<T>(param));

}
// variadic form
template<typename… Ts>
void fwd(Ts&&… params)
{
f(std::forward<Ts>(params)…);
}
f(expression); // if this does one thing,
fwd(expression); // but this does something else, fwd fails
// to perfectly forward expression to f
• We want the forwarded-to function to be able to work with the

originally-passed-in objects.
• Perfect forwarding fails when either of the following occurs:
• Compilers are unable to deduce a type for one or more of

fwd’s parameters
• Compilers deduce the “wrong” type for one or more of fwd’s

parameters
Perfect forwarding failure cases
braced initializers
void f(const std::vector<int>& v); // declaration
f({1, 2, 3}); // fine, “{1, 2, 3}” implicitly converted to std::vector<int>
fwd({1, 2, 3}); // error! Compilers are forbidden from deducing a type for the
// expression {1, 2, 3} in the call to fwd, because fwd’s parameter
// isn’t declared to be a std::initializer_list.
// workaround
auto il = {1, 2, 3}; // il’s type deduced to be std::initializer_list<int>
fwd(il);
Perfect forwarding failure cases
Declaration-only integral static const data member
class Widget {
public:
static const std::size_t MinVals = 28; // Declaration-only, no memory allocated for it.
…
};
std::vector<int> widgetData;
widgetData.reserve(Widget::MinVals); // Compilers will do const propagation.
size_t* pt = &Widget::MinVals; // Link errors. Pointers need memory to address.
f(Widget::MinVals); // Fine, treated as f(28)
fwd(Widget::MinVals); // Link errors. Reference is like pointer. It needs a location to address.
// workaround
const std::size_t Widget::MinVals; // Give a definition in Widget’s .cpp file.
Perfect forwarding failure cases
Overloaded function names and template names
int processVal(int value);
int processVal(int value, int priority);
f(processVal); // Fine
fwd(processVal); // Error! There is no type information on processVal. No type deduction.
template<typename T>
T workOnVal(T param)
{
…
}
fwd(workOnVal); // Error! Which workOnVal instantiation? workOnVal represents many functions.
// workaround
using ProcessFuncType = int (*)(int);
ProcessFuncType processValPtr = processVal; // Use a variable to select which function you want.
fwd(processValPtr); // Fine. processValPtr has type information. It could do type deduction.
fwd(static_cast<ProcessFuncType>(workOnVal)); // Fine. Select workOnVal<int> as parameter.
Perfect forwarding failure cases
Bitfields
struct IPv4Header {
std::uint32_t version:4,
IHL:4,
DSCP:6,
ECN:2,
totalLength:16,
…
};
void f(std::size_t sz);
IPv4Header h;
f(h.totalLength); // Fine.
fwd(h.totalLength); // Error. There’s no way to bind a reference to arbitrary bits.
// workaround
auto length = static_cast<std::uint16_t>(h.totalLength); // Make a copy.
fwd(length); // pass the copy to fwd

Mais conteúdo relacionado

Mais procurados

Pointers,virtual functions and polymorphism cpp
Pointers,virtual functions and polymorphism cppPointers,virtual functions and polymorphism cpp
Pointers,virtual functions and polymorphism cpprajshreemuthiah
 
BUD17-302: LLVM Internals #2
BUD17-302: LLVM Internals #2 BUD17-302: LLVM Internals #2
BUD17-302: LLVM Internals #2 Linaro
 
DWARF Data Representation
DWARF Data RepresentationDWARF Data Representation
DWARF Data RepresentationWang Hsiangkai
 
Bca 2nd sem u-4 operator overloading
Bca 2nd sem u-4 operator overloadingBca 2nd sem u-4 operator overloading
Bca 2nd sem u-4 operator overloadingRai University
 
Java OOPS Concept
Java OOPS ConceptJava OOPS Concept
Java OOPS ConceptRicha Gupta
 
Clean Lambdas & Streams in Java8
Clean Lambdas & Streams in Java8Clean Lambdas & Streams in Java8
Clean Lambdas & Streams in Java8Victor Rentea
 
Static Data Members and Member Functions
Static Data Members and Member FunctionsStatic Data Members and Member Functions
Static Data Members and Member FunctionsMOHIT AGARWAL
 
PPT Functional dan OOP Programming.pptx
PPT Functional dan OOP Programming.pptxPPT Functional dan OOP Programming.pptx
PPT Functional dan OOP Programming.pptxAliefMuhammadAbdilla
 
Understanding react hooks
Understanding react hooksUnderstanding react hooks
Understanding react hooksSamundra khatri
 
Storage classes in C
Storage classes in C Storage classes in C
Storage classes in C Self employed
 
Why TypeScript?
Why TypeScript?Why TypeScript?
Why TypeScript?FITC
 
LLVM Register Allocation
LLVM Register AllocationLLVM Register Allocation
LLVM Register AllocationWang Hsiangkai
 
Storage classes in c language
Storage classes in c languageStorage classes in c language
Storage classes in c languagetanmaymodi4
 

Mais procurados (20)

Pointers,virtual functions and polymorphism cpp
Pointers,virtual functions and polymorphism cppPointers,virtual functions and polymorphism cpp
Pointers,virtual functions and polymorphism cpp
 
Function in C Language
Function in C Language Function in C Language
Function in C Language
 
Storage classes
Storage classesStorage classes
Storage classes
 
BUD17-302: LLVM Internals #2
BUD17-302: LLVM Internals #2 BUD17-302: LLVM Internals #2
BUD17-302: LLVM Internals #2
 
DWARF Data Representation
DWARF Data RepresentationDWARF Data Representation
DWARF Data Representation
 
Bca 2nd sem u-4 operator overloading
Bca 2nd sem u-4 operator overloadingBca 2nd sem u-4 operator overloading
Bca 2nd sem u-4 operator overloading
 
Java OOPS Concept
Java OOPS ConceptJava OOPS Concept
Java OOPS Concept
 
C functions
C functionsC functions
C functions
 
Constructors in C++
Constructors in C++Constructors in C++
Constructors in C++
 
Clean Lambdas & Streams in Java8
Clean Lambdas & Streams in Java8Clean Lambdas & Streams in Java8
Clean Lambdas & Streams in Java8
 
Static Data Members and Member Functions
Static Data Members and Member FunctionsStatic Data Members and Member Functions
Static Data Members and Member Functions
 
PPT Functional dan OOP Programming.pptx
PPT Functional dan OOP Programming.pptxPPT Functional dan OOP Programming.pptx
PPT Functional dan OOP Programming.pptx
 
Understanding react hooks
Understanding react hooksUnderstanding react hooks
Understanding react hooks
 
Storage classes in C
Storage classes in C Storage classes in C
Storage classes in C
 
Why TypeScript?
Why TypeScript?Why TypeScript?
Why TypeScript?
 
Pointers in C
Pointers in CPointers in C
Pointers in C
 
Pointers C programming
Pointers  C programmingPointers  C programming
Pointers C programming
 
Pointer in C++
Pointer in C++Pointer in C++
Pointer in C++
 
LLVM Register Allocation
LLVM Register AllocationLLVM Register Allocation
LLVM Register Allocation
 
Storage classes in c language
Storage classes in c languageStorage classes in c language
Storage classes in c language
 

Destaque

Welcome to Modern C++
Welcome to Modern C++Welcome to Modern C++
Welcome to Modern C++Seok-joon Yun
 
Runtime Symbol Resolution
Runtime Symbol ResolutionRuntime Symbol Resolution
Runtime Symbol ResolutionKen Kawamoto
 
LD_PRELOAD Exploitation - DC9723
LD_PRELOAD Exploitation - DC9723LD_PRELOAD Exploitation - DC9723
LD_PRELOAD Exploitation - DC9723Iftach Ian Amit
 
SSA - PHI-functions Placements
SSA - PHI-functions PlacementsSSA - PHI-functions Placements
SSA - PHI-functions PlacementsWang Hsiangkai
 
Crash dump analysis - experience sharing
Crash dump analysis - experience sharingCrash dump analysis - experience sharing
Crash dump analysis - experience sharingJames Hsieh
 

Destaque (10)

Welcome to Modern C++
Welcome to Modern C++Welcome to Modern C++
Welcome to Modern C++
 
C++11 - STL Additions
C++11 - STL AdditionsC++11 - STL Additions
C++11 - STL Additions
 
dtrace
dtracedtrace
dtrace
 
A22 Introduction to DTrace by Kyle Hailey
A22 Introduction to DTrace by Kyle HaileyA22 Introduction to DTrace by Kyle Hailey
A22 Introduction to DTrace by Kyle Hailey
 
Runtime Symbol Resolution
Runtime Symbol ResolutionRuntime Symbol Resolution
Runtime Symbol Resolution
 
LD_PRELOAD Exploitation - DC9723
LD_PRELOAD Exploitation - DC9723LD_PRELOAD Exploitation - DC9723
LD_PRELOAD Exploitation - DC9723
 
SSA - PHI-functions Placements
SSA - PHI-functions PlacementsSSA - PHI-functions Placements
SSA - PHI-functions Placements
 
Crash dump analysis - experience sharing
Crash dump analysis - experience sharingCrash dump analysis - experience sharing
Crash dump analysis - experience sharing
 
Dynamic Linker
Dynamic LinkerDynamic Linker
Dynamic Linker
 
Introduction to Data Oriented Design
Introduction to Data Oriented DesignIntroduction to Data Oriented Design
Introduction to Data Oriented Design
 

Semelhante a Effective Modern C++

Summary of effective modern c++ item1 2
Summary of effective modern c++ item1 2Summary of effective modern c++ item1 2
Summary of effective modern c++ item1 2Young Ha Kim
 
02a fundamental c++ types, arithmetic
02a   fundamental c++ types, arithmetic 02a   fundamental c++ types, arithmetic
02a fundamental c++ types, arithmetic Manzoor ALam
 
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...Andrey Upadyshev
 
Hot С++: Universal References And Perfect Forwarding
Hot С++: Universal References And Perfect ForwardingHot С++: Universal References And Perfect Forwarding
Hot С++: Universal References And Perfect ForwardingAndrey Upadyshev
 
Glimpses of C++0x
Glimpses of C++0xGlimpses of C++0x
Glimpses of C++0xppd1961
 
presentation_data_types_and_operators_1513499834_241350.pptx
presentation_data_types_and_operators_1513499834_241350.pptxpresentation_data_types_and_operators_1513499834_241350.pptx
presentation_data_types_and_operators_1513499834_241350.pptxKrishanPalSingh39
 
C++ Advanced
C++ AdvancedC++ Advanced
C++ AdvancedVivek Das
 
C intro
C introC intro
C introKamran
 
Problem Solving Techniques
Problem Solving TechniquesProblem Solving Techniques
Problem Solving Techniquesvalarpink
 
Getting started with c++
Getting started with c++Getting started with c++
Getting started with c++K Durga Prasad
 
btech-1picu-5pointerstructureunionandintrotofilehandling-150122010700-conver.ppt
btech-1picu-5pointerstructureunionandintrotofilehandling-150122010700-conver.pptbtech-1picu-5pointerstructureunionandintrotofilehandling-150122010700-conver.ppt
btech-1picu-5pointerstructureunionandintrotofilehandling-150122010700-conver.pptchintuyadav19
 

Semelhante a Effective Modern C++ (20)

Summary of effective modern c++ item1 2
Summary of effective modern c++ item1 2Summary of effective modern c++ item1 2
Summary of effective modern c++ item1 2
 
02a fundamental c++ types, arithmetic
02a   fundamental c++ types, arithmetic 02a   fundamental c++ types, arithmetic
02a fundamental c++ types, arithmetic
 
C++ metaprogramming
C++ metaprogrammingC++ metaprogramming
C++ metaprogramming
 
C++ metaprogramming
C++ metaprogrammingC++ metaprogramming
C++ metaprogramming
 
Advanced pointers
Advanced pointersAdvanced pointers
Advanced pointers
 
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...
[OLD VERSION, SEE DESCRIPTION FOR THE NEWER VERSION LINK] Hot С++: Universal ...
 
Hot С++: Universal References And Perfect Forwarding
Hot С++: Universal References And Perfect ForwardingHot С++: Universal References And Perfect Forwarding
Hot С++: Universal References And Perfect Forwarding
 
Glimpses of C++0x
Glimpses of C++0xGlimpses of C++0x
Glimpses of C++0x
 
presentation_data_types_and_operators_1513499834_241350.pptx
presentation_data_types_and_operators_1513499834_241350.pptxpresentation_data_types_and_operators_1513499834_241350.pptx
presentation_data_types_and_operators_1513499834_241350.pptx
 
C++ Advanced
C++ AdvancedC++ Advanced
C++ Advanced
 
Token and operators
Token and operatorsToken and operators
Token and operators
 
C intro
C introC intro
C intro
 
C programming
C programmingC programming
C programming
 
Problem Solving Techniques
Problem Solving TechniquesProblem Solving Techniques
Problem Solving Techniques
 
Object Oriented Programming with C++
Object Oriented Programming with C++Object Oriented Programming with C++
Object Oriented Programming with C++
 
Getting started with c++
Getting started with c++Getting started with c++
Getting started with c++
 
Getting started with c++
Getting started with c++Getting started with c++
Getting started with c++
 
Data type in c
Data type in cData type in c
Data type in c
 
btech-1picu-5pointerstructureunionandintrotofilehandling-150122010700-conver.ppt
btech-1picu-5pointerstructureunionandintrotofilehandling-150122010700-conver.pptbtech-1picu-5pointerstructureunionandintrotofilehandling-150122010700-conver.ppt
btech-1picu-5pointerstructureunionandintrotofilehandling-150122010700-conver.ppt
 
Input And Output
 Input And Output Input And Output
Input And Output
 

Mais de Wang Hsiangkai

Mais de Wang Hsiangkai (8)

Debug Line Issues After Relaxation.
Debug Line Issues After Relaxation.Debug Line Issues After Relaxation.
Debug Line Issues After Relaxation.
 
Machine Trace Metrics
Machine Trace MetricsMachine Trace Metrics
Machine Trace Metrics
 
Instruction Combine in LLVM
Instruction Combine in LLVMInstruction Combine in LLVM
Instruction Combine in LLVM
 
GCC LTO
GCC LTOGCC LTO
GCC LTO
 
LTO plugin
LTO pluginLTO plugin
LTO plugin
 
GCC GENERIC
GCC GENERICGCC GENERIC
GCC GENERIC
 
Perf File Format
Perf File FormatPerf File Format
Perf File Format
 
Introduction to Perf
Introduction to PerfIntroduction to Perf
Introduction to Perf
 

Último

Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareJim McKeeth
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...masabamasaba
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension AidPhilip Schwarz
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfonteinmasabamasaba
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...Shane Coughlan
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnAmarnathKambale
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxAnnaArtyushina1
 
tonesoftg
tonesoftgtonesoftg
tonesoftglanshi9
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburgmasabamasaba
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyviewmasabamasaba
 
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...WSO2
 
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...WSO2
 
WSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...masabamasaba
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...masabamasaba
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrainmasabamasaba
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2
 

Último (20)

Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
 
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
 
WSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - Keynote
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaS
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 

Effective Modern C++

  • 2. Template Type Deduction • The Type deduction for T is dependent not just on the type of expr, but also on the form of ParamType. • ParamType is a pointer or reference type. • ParamType is a universal reference. • ParamType is neither a pointer nor a reference. template<typename T> void f(ParamType param); f(expr)
  • 3. ParamType is a pointer or reference type 1. If expr’s type is a reference, ignore the reference part. 2. Then pattern-match expr’s type against ParamType to determine T. template<typename T> void f(T& param); int x = 27; const int cx = x; const int& rx = x; f(x); // T is int, param’s type is int& f(cx); // T is const int, param’s type is const int& f(rx); // T is const int, param’s type is const int&
  • 4. ParamType is a universal reference • If expr is an lvalue, both T and ParamType are deduced to be lvalue references. • It’s the only situation in template type deduction where T is deduced to be a reference. • ParamType is declared using the syntax for an rvalue reference, its deduced type is an lvalue reference. • If expr is an rvalue, the Case 1 rules apply. template<typename T> void f(T&& param); int x = 27; const int cx = x; const int& rx = x; f(x); // T is int&, param’s type is int& f(cx); // T is const int&, param’s type is const int& f(rx); // T is const int&, param’s type is const int& f(27); // T is int, param’s type is int&&
  • 5. ParamType is neither a pointer nor a reference template<typename T> void f(T param); int x = 27; const int cx = x; const int& rx = x; f(x); // T is int, param’s type is int f(cx); // T is int, param’s type is int f(rx); // T is int, param’s type is int • Param will be a copy of whatever is passed in. 1. If expr’s type is a reference, ignore the reference part. 2. If expr is const, ignore that. If it’s volatile, also ignore that.
  • 6. Array Arguments template<typename T> void f(T param); // The type of an array passed to a template function by value is deduced // to be a pointer type. const char name[] = “J. P. Briggs”; f(name); // T deduced as const char * // Arrays decay into pointer. template<typename T> void f(T& param); // passed to a template function by reference f(name); // T deduced to be const char [13] // Param’s type is const char (&)[13]
  • 7. Array Arguments template<typename T, std::size_t N> constexpr std::size_t arraySize(T (&)[N]) noexcept { return N; } int keyVals[] = {1, 3, 7, 9, 11, 22, 35}; int mappedVals[arraySize(keyVals)]; std::array<int, arraySize(keyVals)> mappedVals; • The ability to declare references to array enables creation of a template that deduces the number of elements that an array contains.
  • 8. Function Arguments void someFunc(int, double); // type is void (int, double) template<typename T> void f1(T param); // pass by value template<typename T> void f2(T& param); // pass by reference f1(someFunc); // param’s type is void (*)(int, double) // Function types decay into function pointers. f2(someFunc); // param’s type is void (&)(int, double)
  • 9. auto type deduction auto x = 27; const auto cx = x; const auto& rx = x; template<typename T> void x(T param); x(27) template<typename T> void cx(const T param); cx(x); template<typename T> void rx(const T& param); rx(x); With only one exception, auto type deduction is template type deduction. 1. The type specifier is a pointer or reference, but not a universal reference. 2. The type specifier is a universal reference. 3. The type specifier is neither a pointer nor a reference.
  • 10. auto type deduction auto x1 = 27; // int auto x2(27); // int auto x3 = {27}; // std::initializer_list<int> auto x4{27}; // std::initializer_list<int> When the initialiser for an auto-declared variable is enclosed in braces, the deduced type is a std::initializer_lists. auto x = {11, 23, 9}; template<typename T> void x(T param); x({11, 23, 9}); // error! can’t deduce type auto assumes that a braced initialiser represents a std::initializer_list, but template type deduction doesn’t. template<typename T> void x(std::initializer_list<T> param); x({11, 23, 9}); // T deduced as int, param’s type is std::initializer_list<int>
  • 11. auto type deduction C++14: 1. permit auto to indicate that a function’s return type should be deduced. 2. lambdas may use auto in parameter declarations. these use of auto employ template type deduction, not auto type deduction. auto createInitList() { return {1, 2, 3}; // error: can’t deduce type for {1, 2, 3}
 }
  • 12. • Given a name or an expression, decltype tells you the name’s or the expression’s type. • The primary use for decltype is declaring function templates where the function’s return type depends on its parameter types. decltype // C++11 template<typename Container, typename Index> auto autoAndAccess(Container& c, Index i) -> decltype(c[i]) { authenticateUser(); return c[i];
 } // C++14 template<typename Container, typename Index> auto autoAndAccess(Container& c, Index i) { authenticateUser(); return c[i]; // return type deduced from c[i]
 }
  • 13. • To prevent to remove reference-ness of an initialising expression.
 (Rule 3 in template type deduction.) decltype // C++14 template<typename Container, typename Index> decltype(auto) autoAndAccess(Container& c, Index i) { authenticateUser(); return c[i]; // return type deduced from c[i]
 } std::deque<int> d; authAndAccess(d, 5); // d[5] returns an int&
  • 14. decltype // C++14 Widget w; const Widget& cw = w; auto myWidget1 = cw; // myWidget1’s type is Widget // const-ness and reference-ness will be removed decltype(auto) myWidget2 = cw; // myWidget2’s type is const Widget&
  • 15. decltype // Accept left-reference and right-reference container // C++11 template<typename Container, typename Index> auto authAndAccess(Container&& c, Index i) -> decltype(std::forward<Container>(c)[i]) { authenticateUser(); return std::forward<Container>(c)[i]; } // C++14 template<typename Container, typename Index> decltype(auto) authAndAccess(Container&& c, Index i) { authenticateUser(); return std::forward<Container>(c)[i]; }
  • 16. decltype • Applying decltype to a name yields the declared type for that name. • For lvalue expressions more complicated than names, decltype 
 ensures that the type reported is always an lvalue reference. // C++14 decltype(auto) f1() { int x = 0; … return x; // decltype(x) is int, so f1 returns int } decltype(auto) f2() { int x = 0; … return (x); // decltype((x)) is int&, so f2 returns int& // return a reference to a local variable! (undefined behavior) }
  • 17. auto - must be initialised • auto variables have their type deduced from their initialiser, so they
 must be initialised. int x1; // potentially uninitialised auto x2; // error! initialiser required auto x3 = 0; // fine, x’s value is well-defined
  • 18. auto - avoid verbose template<typename It> void dwim(It b, It e) { while (b != e) { typename std::iterator_traits<It>::value_type currValue = *b; … } } template<typename It> void dwim(It b, It e) { while (b != e) { auto currValue = *b; … } } • avoid verbose variable declarations
  • 19. auto - hold a closure • std::function is a template in the C++11 Standard Library that
 generalised the idea of a function pointer. • Using std::function is not the same as using auto. • An auto-declared variable holding a closure has the same type as
 the closure, it uses only as much memory as the closure requires. std::function<bool(const std::unique_ptr<Widget>&, const std::unique_ptr<Widget>&)> derefUPLess = [](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2) { return *p1 < *p2; }; // C++11 auto derefUPLess = [](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2) { return *p1 < *p2; }; // C++14 auto derefUPLess = [](const auto& p1, const auto& p2) { return *p1 < *p2; };
  • 20. auto • The type of a std::function-declared variable holding a closure is an
 instantiation of the std::function template, and that has a fixed size
 for any given signature. • The std::function approach is generally bigger and slower than the
 auto approach, and it may yield out-of-memory exceptions. std::function<bool(const std::unique_ptr<Widget>&, const std::unique_ptr<Widget>&)> derefUPLess = [](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2) { return *p1 < *p2; }; // C++11 auto derefUPLess = [](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2) { return *p1 < *p2; }; // C++14 auto derefUPLess = [](const auto& p1, const auto& p2) { return *p1 < *p2; };
  • 21. auto • Use auto to avoid potential bugs and performance issues. std::vector<int> v; unsigned sz = v.size(); // potential bugs. unsigned is 32-bits on 64-bits machine // v.size()’s type is std::vector<int>::size_type auto sz = v.size(); // sz’s type is std::vector<int>::size_type std::unordered_map<std::string, int> m; // Key value is const, so actual type is std::pair<const std::string, int> // There will be a temporary variable of std::pair<std::string, int> // Every loop has an extra constructor and destructor for (const std::pair<std::string, int>& p : m) { … } // avoid temporary variable // simply bind the reference p to each element in m for (const auto& p : m) { … }
  • 22. auto • operator[] for std::vector<bool> doesn’t return a reference to
 an element of the container. C++ forbids references to bits. • It returns an object of type std::vector<bool>::reference std::vector<bool> features(const Widget& w); Widget w; auto highPriority = features(w)[5]; // deduced type is std::vector<bool>::reference // highPriority is a reference to a temporary
 // variable. It is a dangling pointer. processWidget(w, highPriority); // undefined behaviour! std::vector<bool> features(const Widget& w); Widget w; bool highPriority = features(w)[5]; // std::vector<bool>::reference implicit
 // convert to bool processWidget(w, highPriority);
  • 23. auto • std::vector<bool>::reference is a proxy class that exists for the
 purpose of emulating and augmenting the behaviour of some other type. • As a general rule, proxy classes don’t play well with auto. • Objects of such classes are often not designed to live longer than a
 single statement. // solution std::vector<bool> features(const Widget& w); Widget w; auto highPriority = static_cast<bool>(features(w)[5]); // explicitly typed processWidget(w, highPriority); // avoid this auto someVar = expression of proxy class type
  • 24. Brace Initialiser • There are three methods to initialise variables. int x(0); // initialiser is in parentheses int y = 0; // initialiser follows “=“ int z{ 0 }; // initialiser is in braces • There were some situations where c++98 had no way to express a 
 desired initialisation. std::vector<int> v{1, 3, 5}; // (C++11) v’s initial content is 1, 3, 5
  • 25. Brace Initialiser • Brace initialiser is universal initialiser. // C++11. Braces can be used to initialise non-static data members. class Widget { … private: int x{0}; // fine, x’s default value is 0 int y = 0; // also fine int z(0); // error! }; // uncopyable objects may be initialised using braces or parentheses. std::atomic<int> ai1{0}; // fine std::atomic<int> ai2(0); // fine std::atomic<int> ai3 = 0; // error!
  • 26. Brace Initialiser • Brace initialiser prohibits implicit narrowing conversions. double x, y, z; int sum1{x + y + z}; // error! sum of doubles may not be expressible as int int sum2(x + y + z); // okay int sum3 = x + y + z; // okay
  • 27. Brace Initialiser • std::initializer_list overshadows other constructors to the point
 where the other overloads may hardly be considered. class Widget { public: Widget(int i, bool b); Widget(int i, double d); Widget(std::initializer_list<long double> il); … }; Widget w1(10, true); // call first ctor Widget w2{10, true}; // call std::initializer_list ctor Widget w3(10, 5.0); // call second ctor Widget w4{10, 5.0}; // call std::initializer_list ctor
  • 28. Brace Initialiser • Most developers end up choosing one kind of delimiter as a default, using
 the other only when they have to.
  • 29. nullptr • 0 and NULL are integral type, not pointer type. • nullptr’s type is std::nullptr_t.
 The type implicitly converts to all raw pointer types. void f(int); void f(bool); void f(void*); f(0); // call f(int), not f(void*) f(NULL); // might not compile, but typically calls f(int)
  • 30. nullptr • improve code clarity auto result = findRecord(); if (result == 0) { // the return value may be int or pointer … } auto result = findRecord(); if (result == nullptr) { // the return value must be pointer, it is more clear. …
 }
  • 31. nullptr • template type deduction template<typename FuncType, typename MuxType, typename PtrType> auto lockAndCall(FuncType func, MuxType mutex, PtrType ptr) -> decltype(func(ptr)) { using MuxGuard = std::lock_guard<MuxType>; MuxGuard g(mutex); // lock mutex return func(ptr); // call function
 } // unlock mutex int f1(std::shared_ptr<Widget> spw); // call these only when double f2(std::unique_ptr<Widget> upw); // the appropriate bool f3(Widget* pw); // mutex is locked std::mutex f1m, f2m, f3m; auto result1 = lockAndCall(f1, f1m, 0); // error! ptr’s type is deduced to int auto result2 = lockAndCall(f2, f2m, NULL); // error! auto result3 = lockAndCall(f3, f3m, nullptr); // fine. ptr’s type is std::nullptr_t
 // implicitly convert to Widget*
  • 32. Alias Declarations // use typedef typedef std::unique_ptr<std::unordered_map<std::string, std::string>> UPtrMapSS; typedef void (*FP)(int, const std::string&); // use alias declaration using UPtrMapSS = std::unique_ptr<std::unordered_map<std::string, std::string>> using FP = void (*)(int, const std::string&);
  • 33. Alias Declarations // alias declaration template<typename T> using MyAllocList = std::list<T, MyAlloc<T>>; MyAllocList<Widget> lw; // typedef template<typename T> struct MyAllocList { typedef std::list<T, MyAlloc<T>> type; }; MyAllocList<Widget>::type lw; • Alias declarations may be templatized, while typedefs cannot.
  • 34. Alias Declarations template<typename T> struct MyAllocList { typedef std::list<T, MyAlloc<T>> type; }; template<typename T> class Widget { private: typename MyAllocList<T>::type list; // MyAllocList<T> may be a specialization. // “type” may be a data member of it // The names of dependent types must // be preceded by typename … }; • typedef inside a template class
  • 35. Scoped enum enum Color { black, white, red }; // unscoped enum auto white = false; // error! white already declared in this scope enum class Color { black, white, red }; // scoped enum. avoid name pollution auto white = false; // fine. • The name of unscoped enum belong to the scope containing the enum.
  • 36. Scoped enum enum class Color { black, white, red }; // scoped enum std::vector<std::size_t> primeFactors(std::size_t x); Color c = Color::white; // fine. if (c < 14.5) { // error! can’t convert to double auto factors = primeFactors(c); // error! can’t convert to std::size_t … } • Scoped enum is strongly typed
  • 37. Scoped enum enum class Color; // fine. enum Color; // error! • Scoped enum could be forward-declared • If unscoped enum wants to be forward-declared, it needs to specify
 underlying type. enum Color: std::uint8_t; // forward-declaration for unscoped enum
  • 38. deleted function // C++98 template <class charT, class traits = char_traits<charT> > class basic_ios : public ios_base { public: … private; basic_ios(const basic_ios& ); // not defined (link-time error) basic_ios& operator=(const basic_ios& ); // not defined } • prevent calling some particular functions // C++11 template <class charT, class traits = char_traits<charT> > class basic_ios : public ios_base { public: // C++ checks accessibility before deleted status. // Let compiler show more clear messages. … basic_ios(const basic_ios& ) = delete; // if used, it’s a compile-time basic_ios& operator=(const basic_ios& ) = delete; // error. … }
  • 39. deleted function template<typename T> void processPointer(T* ptr); template<> void processPointer<void>(void*) = delete; template<> void processPointer<char>(char*) = delete; • prevent use of template instantiations that should be disabled // template member function class Widget { public: template<typename T> void processPointer(T* ptr) { … } }; template<> void Widget::processPointer<void>(void*) = delete;
  • 40. override class Base { public: virtual void mf1() const; virtual void mf2(int x); virtual void mf3() &; }; class Derived : public Base { public: virtual void mf1() const override; virtual void mf2(int x) override; virtual void mf3() & override; }; • make explicit that a derived class function is supposed to override
 a base class version
  • 41. Prefer const_iterators to iterators std::vector<int> values auto it = std::find(values.cbegin(), values.cend(), 1983); values.insert(it, 1998); • The standard practice of using const whenever possible dictates that you
 should use const_iterators any time you need an iterator. • There is no portable conversion from a const_iterator to an iterator,
 not even with a static_cast.
  • 42. constexpr objects int sz; constexpr auto arraySize1 = sz; // error! sz’s value not known at compilation std::array<int, sz> data1; // error! constexpr auto arraySize2 = 10; std::array<int, arraySize2> data2; // fine. arraySize2 is constexpr int sz2; const auto arraySize = sz2; // fine. arraySize is const copy of sz2 std::array<int, arraySize> data; // error! arraySize’s value not known at compilation • constexpr indicates a value that’s not only constant, it’s known
 during compilation. • constexpr objects are, in fact, const, and they do have values that are
 known at compile time.
  • 43. constexpr functions constexpr int pow(int base, int exp) noexcept { … } constexpr auto numConds = 5; std::array<int, pow(3, numConds)> results; // call pow function at compile-time auto base = readFromDB(“base”); auto exp = readFromDB(“exponent”); auto baseToExp = pow(base, exp); // call pow function at runtime • constexpr functions produce compile-time constants when they are called
 with compile-time constants. If they’re called with values not known until
 runtime, they produce runtime values.
  • 44. constexpr // C++11 constexpr int pow(int base, int exp) noexcept { return (exp == 0 ? 1 : base * pow(base, exp - 1)); } // C++14 constexpr int pow(int base, int exp) noexcept { auto result = 1; for (int i = 0; i < exp; i++) result *= base; return result;
 } • In C++11, constexpr functions may contain no more than a single
 executable statement: a return.
  • 45. constexpr class Point { public: constexpr Point(double xVal = 0, double yVal = 0) noexcept : x(xVal), y(yVal) {} constexpr double xValue() const noexcept { return x; } constexpr double yValue() const noexcept { return y; } void setX(double newX) noexcept { x = newX; } // setX could be constexpr in C++14 void setY(double newY) noexcept { y = newY; } private: double x, y; }; constexpr Point p1(9.4, 27.7); // “runs” constexpr actor during compilation constexpr Point p2(28.8, 5.3); constexpr Point midpoint(const Point& p1, const Point& p2) noexcept { return { (p1.xValue() + p2.xValue()) / 2, (p1.yValue() + p2.yValue()) / 2}; } constexpr auto mid = midpoint(p1, p2); • constructors and other member functions may be constexpr
  • 46. make const member functions thread safe • roots doesn’t change Polynomial object on which it operates, but as
 part of its caching activity, it may need to modify rootVals and
 rootsAreValid. • roots is declared const, but it’s not thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: mutable bool rootsAreValid{ false }; mutable RootsType rootVals{};
 };
  • 47. make const member functions thread safe • std::mutex is a move-only type. A side effect of adding m to Polynomial
 is that Polynomial loses the ability to be copied. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { std::lock_guard<std::mutex> g(m); if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: mutable std::mutex m; mutable bool rootsAreValid{ false }; mutable RootsType rootVals{};
 };
  • 48. make const member functions thread safe • std::atomic variables are often less expensive than mutex. • For a single variable or memory location requiring synchronization, use
 of a std::atomic is adequate. class Point { public: double distanceFromOrigin() const noexcept { ++callCount; return std::sqrt((x * x) + (y * y)); }; private: mutable std::atomic<unsigned> callCount{ 0 }; double x, y; };
  • 49. special member function generation • the default constructor • the destructor • the copy constructor • the copy assignment operator • the move constructor (C++11) • the move assignment operator (C++11) class Widget { public: Widget(Widget&& rhs); // move constructor widget& operator=(Widget&& rhs); // move assignment operator };
  • 50. special member function generation • the move constructor (C++11) • the move assignment operator (C++11) • perform “memberwise moves” on the non-static data members • “memberwise moves” are more like member wise move requests. • memberwise move consists of move operations on data members
 and base classes that support move operations, but a copy operation
 for those that don’t. • the two move operations are not independent. • declaring a move constructor prevents a move assignment operator
 from being generated. • declaring a move assignment operator prevents compilers from
 generating a move constructor. • move operations won’t be generated for any class that explicitly declares
 a copy operation. • C++11 does not generate move operations for a class with a user-declared
 destructor. • The Rule of Three: if you declare any of a copy constructor, copy
 assignment operator, or destructor, you should declare all three.
  • 51. special member function generation • So move operations are generated for classes only if these three things are
 true: • No copy operations are declared in the class. • No move operations are declared in the class. • No destructor is declared in the class.
  • 52. std::unique_ptr • std::unique_ptr embodies exclusive ownership semantic. • Copying a std::unique_ptr isn’t allowed. • By default, that destruction would take place via delete, but, during
 construction, std::unique_ptr objects can be configured to use custom
 deleters. auto delInvmt = [](Investment* pInvestment) // accept a raw pointer as argument { makeLogEntry(pInvestment); delete pInvestment; }; template<typename… Ts> std::unique_ptr<Investment, decltype(delInvmt)> // specified in unique_ptr makeInvestment(Ts&&… params) { std::unique_ptr<Investment, decltype(delInvmt)> pInv(nullptr, delInvmt); if ( /* a Stock object should be created */ ) { pInv.reset(new Stock(std::forward<Ts>(params)…); } return pInv; }
  • 53. std::unique_ptr • Deleters that are function pointers generally cause the size of a
 std::unique_ptr to grow from one word to two. • For deleters that are function objects, the change in size depends on
 how much state is stored in the function object. • Stateless function objects (lambda expressions) incur no size penalty.
  • 54. std::unique_ptr • std::unique_ptr comes in two forms, one for individual objects )
 std::unique_ptr<T>) and one for arrays (std::unique_ptr<T[]>). • One of its most attractive features is that it easily and efficiently converts to
 a std::shared_ptr. • std::shared_ptr can’t work with arrays.
  • 55. std::shared_ptr • When the last std::shared_ptr pointing to an object stops pointing there,
 that std::shared_ptr destroys the object it points to. • A std::shared_ptr can tell whether it’s the last one pointing to a resource
 by consulting the resource’s reference count. • std::shared_ptr are twice the size of a raw pointer. • Memory for the reference count must be dynamically allocated. • Increments and decrements of the reference count must be atomic.
  • 56. std::shared_ptr Custom Deleters • For std::unique_ptr, the type of the deleter is part of the type of the smart
 pointer. For std::shared_ptr, it’s not. • Specifying a custom deleter doesn’t change the size of a std::shared_ptr
 object. auto loggingDel = [](Widget* pw) { makeLogEntry(pw); delete pw; }; std::unique_ptr<Widget, decltype(loggingDel)> upw(new Widget, loggingDel); std::shared_ptr<Widget> spw(new Widget, loggingDel); pointer to T pointer to Contrl Block std::shared_ptr<T> T Object Reference Count Weak Count Custom Deleter
  • 57. std::shared_ptr Control Block • An object’s control block is set up by the function creating the first
 std::shared_ptr to the object. • std::make_shared always creates a control block. • A control block is created when a std::shared_ptr is constructed from
 a unique-ownership pointer. • When a std::shared_ptr constructor is called with a raw pointer, it
 creates a control block. auto pw = new Widget; // The creation of the raw pointer is bad. std::shared_ptr<Widget> spw1(pw, loggingDel); // create control block std::shared_ptr<Widget> spw2(pw, loggingDel); // create 2nd control block // It will cause undefined behavior. std::shared_ptr<Widget> spw1(new Widget, loggingDel); // direct use of new std::shared_ptr<Widget> spw2(spw1); // spw2 uses same control block as spw1
  • 58. std::shared_ptr std::vector<std::shared_ptr<Widget>> processedWidgets; class Widget { public: void process(); … }; void Widget::process() { processWidgets.emplace_back(this); // pass raw pointer to shared_ptr // This is wrong! // It may cause undefined behaviour.
 }
  • 59. std::shared_ptr std::vector<std::shared_ptr<Widget>> processedWidgets; // The Curiously Recurring Template Pattern (CRTP) class Widget : public std::enable_shared_from_this<Widget> { public: // To prevent clients from calling member functions that invoke shared_from_this // before a std::shared_ptr points to the object. template<typename… Ts> static std::shared_ptr<Widget> create(Ts&&… params); // factory method … void process(); … private: … // private ctors }; void Widget::process() { processWidgets.emplace_back(shared_from_this()); // There must be an existing // std::shared_ptr that points // to the current object.
 }
  • 60. std::weak_ptr • A pointer like std::shared_ptr that doesn’t affect an object’s
 reference count. • std::weak_ptr isn’t a standalone smart pointer. It’s an augmentation of
 std::shared_ptr. auto spw = std::make_shared<Widget>(); std::weak_ptr<Widget> wpw(spw); // wpw points to same Widget as spw. spw = nullptr; // wpw now dangles. if (wpw.expired()) … // if wpw doesn’t point to an object… // “check then create” should be atomic. // creating a shared_ptr from the std::weak_ptr // 1. use weak_ptr::lock std::shared_ptr<Widget> spw1 = wpw.lock(); // if wpw’s expired, spw1 is null auto spw2 = wpw.lock(); // same as above, but uses auto // 2. use constructor std::shared_ptr<Widget> spw3(wpw); // if wpw’s expired, throw std::bad_weak_ptr
  • 61. std::weak_ptr • Caching • Observer design pattern • keep a list of observers • Backward pointer // caching example std::unique_ptr<const Widget> loadWidget(WidgetID id); // costly version std::shared_ptr<const Widget> fastLoadWidget(WidgetID id) // caching version { static std::unordered_map<WidgetID, std::weak_ptr<const Widget>> cache; auto objPtr = cache[id].lock(); if (!objPtr) { objPtr = loadWidget(id); // load the object cache[id] = objPtr; // cache it } return objPtr;
 } A B C std::shared_ptr std::shared_ptr std::weak_ptr
  • 62. std::make_unique && std::make_shared • std::make_shared is part of C++11. • std::make_unique is part of C++14. • This form of the function doesn’t support arrays or custom deleters. • std::allocate_shared acts like std::make_shared, except
 its first argument is an allocator object to be used for the dynamic
 memory allocation. // make_unique implementation for C++11 template<typename T, typename… Ts> std::unique_ptr<T> make_unique(Ts&&… params) { return std::unique_ptr<T>(new T(std::forward<Ts>(params)…));
 } auto upw1(std::make_unique<Widget>()); // with make function std::unique_ptr<Widget> upw2(new Widget); // without make function auto spw1(std::make_shared<Widget>()); // with make function std::shared_ptr<Widget> spw2(new Widget); // without make function
  • 63. std::make_unique && std::make_shared int computePriority(); // Before calling processWidget, // * The expression “new Widget” must be evaluated // * The constructor for the std::shared_ptr<Widget> // * computePriority must run processWidget(std::shared_ptr<Widget>(new Widget), computePriority()); // If compiler emits code as follow // 1. Perform “new Widget” // 2. Execute computePriority // 3. Run std::shared_ptr constructor // If (2) produces an exception, the dynamically allocated memory will be leaked. // std::make_shared has no such problems. processWidget(std::make_shared<Widget>(), computePriority());
  • 64. std::make_unique && std::make_shared // Two allocations. // 1. allocate memory for Widget // 2. allocate memory for control block of std::shared_ptr std::shared_ptr<Widget> spw(new Widget); // One allocation suffices. // Caveat: Widget memory will not free until no one uses control block. auto spw = std::make_shared<Widget>();
  • 65. std::make_unique && std::make_shared • None of the make functions permit the specification of custom deleters. • Within the make functions, the perfect forwarding code uses parentheses,
 not braces. • Using make functions to create objects of types with class-specific versions
 of operator new and operator delete is typically a poor idea.
  • 66. std::make_unique && std::make_shared • Exception safe code without make functions. void processWidget(std::shared_ptr<Widget> spw, int priority); void cusDel(Widget *ptr); // custom deleter // Put allocation of the Widget and the construction of the std::shared_ptr // into one statement. std::shared_ptr<Widget> spw(new Widget, cusDel); // exception safe processWidget(std::move(spw), computePriority()); // use move() to turn lvalue // to rvalue. It is more // efficient. // Because processWidget’s first parameter is passed by value, construction // from an rvalue entails only a move, while construction from an lvalue requires // a copy.
  • 67. Pimpl Idiom // Original class class Widget { public: Widget(); … private: std::string name; std::vector<double> data; Gadget g1, g2, g3; }; // Pimpl Idiom class Widget { public: Widget(); ~Widget(); // door is needed private: struct Impl; // declare implementation struct Impl *pImpl; // and pointer to it };
  • 68. Pimpl Idiom (raw pointer) #include “widget.h” // in impl. file “widget.cpp” #include “gadget.h” #include <string> #include <vector> struct Widget::Impl { std::string name; std::vector<double> data; Gadget g1, g2, g3; }; Widget::Widget() : pImpl (new Impl) {} Widget::~Widget() { delete pImpl; }
  • 69. Pimpl Idiom (std::unique_ptr) class Widget { // in header “widget.h” public: Widget(); ~Widget(); // declaration only Widget(Widget&& rhs); // declaration only Widget& operator=(Widget&& rhs); Widget(const Widget& rhs); // declaration only Widget& operator=(const Widget& rhs); private: struct Impl; // incomplete type declaration std::unique_ptr<Impl> pImpl; };
  • 70. Pimpl Idiom (std::unique_ptr) #include “widget.h” // in impl. file “widget.cpp” #include “gadget.h” #include <string> #include <vector> struct Widget::Impl { std::string name; std::vector<double> data; Gadget g1, g2, g3; }; Widget::Widget() : pImpl(std::make_unique<Impl>()) {} // dtor use static_assert to ensure that the // raw pointer is complete type Widget::~Widget() = default; // destructor needs to see complete type // generate code to destroy pImpl when exception arises // (need complete type) Widget::Widget(Widget&& rhs) = default; // need to destroy the object pointed to by pImpl // before reassigning it. (need complete type) Widget& Widget::operator=(Widget&& rhs) = default; Widget::Widget(const Widget& rhs) // deep copy : pImpl(std::make_unique<Impl>(*rhs.pImpl)) {} Widget& Widget::operator=(const Widget& rhs) { *pImpl = *rhs.pImpl; return *this;
 }
  • 71. std::move // C++11 template<typename T> typename remove_reference<T>::type&& // ensuring that && is applied to a type that isn’t a reference. move(T&& param) { using ReturnType = typename remove_reference<T>::type&&; return static_cast<ReturnType>(param); } // C++14 template<typename T> decltype(auto) move(T&& param) { using ReturnType = remove_reference_t<T>&&; return static_cast<ReturnType>(param);
 } • std::move does nothing but cast its argument to an rvalue.
  • 72. std::move class Annotation { public: explicit Annotation(const std::string text) : value(std::move(text)) // move will convert to const std::string&& { … } // It will call copy ctor, not move ctor. … private: std::string value; }; class string { public: … string(const string& rhs); // copy ctor string(string&& rhs); // move ctor }; • Don’t declare objects const if you want to be able to move from them. • std::move doesn’t guarantee that the object it’s casting will be eligible
 to be moved.
  • 73. std::forward void process(const Widget& lvalArg); void process(Widget&& rvalArg); template<typename T> void logAndProcess(T&& param) // universal reference { auto now = std::chrono::system_clock::now(); makeLogEntry(“Calling ‘process’”, now); process(std::forward<T>(param)); // T will convey rvalue or lvalue information. } Widget w; logAndProcess(w); // call with lvalue logAndProcess(std::move(w)); // call with rvalue • std::forward casts to an rvalue only if its argument was initialised with
 an rvalue.
  • 74. rvalue reference and universal reference void f(Widget&& param); // rvalue reference Widget&& var1 = Widget(); // rvalue reference auto&& var2 = var1; // universal reference (auto declaration) template<typename T> void f(std::vector<T>&& param); // rvalue reference template<typename T> void f(T&& param); // universal reference (function template parameter) template<typename T> void f(const T&& param); // rvalue reference • Universal references arise in two context. • function template parameters (It must be precisely “T&&”.) • auto declarations • What these contexts have in common is the presence of type deduction.
  • 75. rvalue reference and universal reference template<class T, class Allocator = allocator<T>> class vector { public: void push_back(T&& x); // rvalue reference template <class… Args> void emplace_back(Args&&… args); // universal reference … }; auto timeFuncInvocation = [](auto&& func, auto&&… params) // universal reference { // start timer; std::forward<decltype(func)>(func)( std::forward<decltype(params)>(params)…); // stop timer and record elapsed time; };
  • 76. Use std::forward on universal reference // Example 1 class Widget { public: template<typename T> void setName(T&& newName) // universal reference { name = std::move(newName); } // bad idea to use move() on universal reference … private: std::string name; }; std::string getWidgetName(); Widget w; auto n = getWidgetName(); w.setName(n); // move n into w // n’s value now unknown (potential error!) // Example 2 template<typename T> Fraction reduceAndCopy(T&& frac) { frac.reduce(); return std::forward<T>(frac); // move rvalue into return value, copy lvalue
 }
  • 77. Use std::move on rvalue reference Matrix operator+(Matrix&& lhs, const Matrix& rhs) { lhs += rhs; return std::move(lhs); // move lhs into return value (more efficient) } Matrix operator+(Matrix&& lhs, const Matrix& rhs) { lhs += rhs; return lhs; // copy lhs into return value
 } Widget makeWidget() { Widget w; … return std::move(w); // DO NOT apply move() on local variable. // It will hinder RVO. // If the conditions for the RVO are met, but // compiler choose not to perform copy elision, // the object being returned must be treated as // an rvalue. (std::move is implicitly applied.) }
  • 78. Avoid overloading on universal references template<typename T> void logAndAdd(T&& name) // (1) { auto now = std::chrono::system_clock::now(); log(now, “logAndAdd”); names.emplace(std::forward<T>(name));
 } void logAndAdd(int idx) // (2) { auto now = std::chrono::system_clock::now(); log(now, “logAndAdd”); names.emplace(nameFromIdx(idx)); } short nameIdx; logAndAdd(nameIdx); // Error! It will be resolved to logAndAdd(short&& name). Match (1). // Exactly match (1). // Promotion to match (2). Function (1) is better than (2). • Functions taking universal references are the greediest functions in C++.
 They instantiate to create matches for almost any type of argument.
  • 79. Avoid overloading on universal references class Person { public: template<typename T> explicit Person(T&& n) // (1) : name(std::forward<T>(n)) { … } explicit Person(int idx) // (2) : name(nameFromIdx(idx)) { … } Person(const Person& rhs); // (3) copy ctor (compiler-generated) Person(Person&& rhs); // (4) move ctor (compiler-generated) … private: std::string name; }; Person p(“Nancy”); auto cloneOfP(p); // Error! It will be resolved to Person(Person&). Match (1). const Person cp(“Nancy”); auto cloneOfP(cp); // Calls copy ctor. Match (1) and (3). // Normal function is preferred.
  • 80. Avoid overloading on universal references class Person { public: template<typename T> explicit Person(T&& n) // (1) : name(std::forward<T>(n)) { … } explicit Person(int idx) // (2) : name(nameFromIdx(idx)) { … } Person(const Person& rhs); // (3) copy ctor (compiler-generated) Person(Person&& rhs); // (4) move ctor (compiler-generated) … private: std::string name; }; class SpecialPerson : public Person { pubic: SpecialPerson(const SpecialPerson& rhs) // copy ctor : Person(rhs) { … } // Error. Calls (1), not base class copy ctor (3) SpecialPerson(SpecialPerson&& rhs) // move ctor : Person(std::move(rhs)) { … } // Error. Calls (1), not base class move ctor (4) };
  • 81. Tag Dispatch template<typename T> void logAndAdd(T&& name) { logAndAddImpl( std::forward<T>(name), std::is_integral<typename std::remove_reference<T>::type>() // remove reference before // pass to is_integral<T>() ); } template<typename T> void logAndAddImpl(T&& name, std::false_type) { auto now = std::chrono::system_clock::now(); log(now, “logAndAdd”); names.emplace(std::forward<T>(name)); } void logAndAddImpl(int idx, std::true_type) { logAndAdd(nameFromIdx(idx));
 }
  • 82. std::enable_if class Person { public: // syntax: enable_if<condition>::type // is_base_of<T1, T2>::value is true if T2 is derived from T1 // std::decay<T>::type removes reference and cv-qualifiers(i.e., const or volatile) from T template< typename T, typename = typename std::enable_if< !std::is_base_of<Person, typename std::decay<T>::type >::value && !std::is_integral<typename std::remove_reference<T>::type >::value >::type > explicit Person(T&& n); // universal reference constructor …
 }; • std::enable_if gives you a way to force compilers to behave as if a
 particular template didn’t exist.
  • 83. Reference collapsing When a lvalue is passed as an argument, T is deduced to be an lvalue reference. When an rvalue is passed, T is deduced to be a non-reference. template<typename T> void func(T&& param); Widget widgetFactory(); // function returning rvalue Widget w; // a variable (an lvalue) func(w); // T deduced to be Widget&. // func(Widget& && param) => func(Widget& param) func(widgetFactory()); // T deduced to be Widget • Reference to reference is illegal. • Compiler may produce reference to reference in particular contexts. • If either reference is an lvalue reference, the result is an lvalue reference.
 Otherwise the result is an rvalue reference. • A universal reference isn’t a new kind of reference, it’s actually an rvalue
 reference in a context where two conditions are satisfied: • Type deduction distinguishes lvalues from rvalues. Lvalues of type T
 are deduced to have type T&, while rvalues of type T yield T as their
 deduced type. • Reference collapsing occurs.
  • 84. Perfect forwarding failure cases template<typename T> void fwd(T&& param) { f(std::forward<T>(param));
 } // variadic form template<typename… Ts> void fwd(Ts&&… params) { f(std::forward<Ts>(params)…); } f(expression); // if this does one thing, fwd(expression); // but this does something else, fwd fails // to perfectly forward expression to f • We want the forwarded-to function to be able to work with the
 originally-passed-in objects. • Perfect forwarding fails when either of the following occurs: • Compilers are unable to deduce a type for one or more of
 fwd’s parameters • Compilers deduce the “wrong” type for one or more of fwd’s
 parameters
  • 85. Perfect forwarding failure cases braced initializers void f(const std::vector<int>& v); // declaration f({1, 2, 3}); // fine, “{1, 2, 3}” implicitly converted to std::vector<int> fwd({1, 2, 3}); // error! Compilers are forbidden from deducing a type for the // expression {1, 2, 3} in the call to fwd, because fwd’s parameter // isn’t declared to be a std::initializer_list. // workaround auto il = {1, 2, 3}; // il’s type deduced to be std::initializer_list<int> fwd(il);
  • 86. Perfect forwarding failure cases Declaration-only integral static const data member class Widget { public: static const std::size_t MinVals = 28; // Declaration-only, no memory allocated for it. … }; std::vector<int> widgetData; widgetData.reserve(Widget::MinVals); // Compilers will do const propagation. size_t* pt = &Widget::MinVals; // Link errors. Pointers need memory to address. f(Widget::MinVals); // Fine, treated as f(28) fwd(Widget::MinVals); // Link errors. Reference is like pointer. It needs a location to address. // workaround const std::size_t Widget::MinVals; // Give a definition in Widget’s .cpp file.
  • 87. Perfect forwarding failure cases Overloaded function names and template names int processVal(int value); int processVal(int value, int priority); f(processVal); // Fine fwd(processVal); // Error! There is no type information on processVal. No type deduction. template<typename T> T workOnVal(T param) { … } fwd(workOnVal); // Error! Which workOnVal instantiation? workOnVal represents many functions. // workaround using ProcessFuncType = int (*)(int); ProcessFuncType processValPtr = processVal; // Use a variable to select which function you want. fwd(processValPtr); // Fine. processValPtr has type information. It could do type deduction. fwd(static_cast<ProcessFuncType>(workOnVal)); // Fine. Select workOnVal<int> as parameter.
  • 88. Perfect forwarding failure cases Bitfields struct IPv4Header { std::uint32_t version:4, IHL:4, DSCP:6, ECN:2, totalLength:16, … }; void f(std::size_t sz); IPv4Header h; f(h.totalLength); // Fine. fwd(h.totalLength); // Error. There’s no way to bind a reference to arbitrary bits. // workaround auto length = static_cast<std::uint16_t>(h.totalLength); // Make a copy. fwd(length); // pass the copy to fwd