(plus or minus two) modern c++ boilerplate or,ajo/disseminate/c++now2015.pdf · the rule of zero...

69
The Rule of Seven (plus or minus two) or, Modern C++ Boilerplate http://www.club.cc.cmu.edu/~ajo/disseminate/C++Now2015.pdf

Upload: others

Post on 25-Jul-2020

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The Rule of Seven(plus or minus two)

orModern C++ Boilerplate

httpwwwclubcccmuedu~ajodisseminateC++Now2015pdf

The Rule of Zero

Write your classes in a way that you do not need to declaredefine neither a destructor nor a copymove constructor or copymove assignment operator

mdash Peter Sommerlad 2013

httpwikihsrchPeterSommerladfilesMeetingCPP2013_SimpleC++pdf

The Rule of Zero (SRP)Classes that have custom destructors copymove constructors or copymove assignment operators should deal exclusively with ownership

Other classes should not have custom destructors copymove constructors or copymove assignment operators

httpflamingdangerzonecomcxx11rule-of-zerohttpaccuorgindexphpjournals1896

mdash R Martinho Fernandes

The Rule of Six (Howard Hinnant)Know when the compiler is defaulting or deleting special members for you and what defaulted members will do

Always define or delete a special member when the compilerrsquos implicit action is not correct

Give tender loving care to each of the 6 special members even if the result is to let the compiler handle it

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

mdash Howard Hinnant 2014

What are the special member functions

Default constructor Destructor

Copy constructor Copy assignment operator

Move constructor Move assignment operator

NOT special member functions

Other constructors Other assignment operators Member swap() Non-member swap() Comparison operators

NOT special member functions

Other constructors Other assignment operators Member swap() Non-member swap() Comparison operators

Wersquoll talk about these too but not quite yet

Sample code for testingdefine CE constexpr Clang doesnt like this but GCCrsquos okay with itdefine NE noexcept

struct Instruments CE Instruments() NE puts(Instruments default-ctor) CE Instruments(const Instrumentsamp) NE puts(Instruments copy-ctor) CE Instruments(Instrumentsampamp) NE puts(Instruments move-ctor) CE Instrumentsamp operator=(const Instrumentsamp) NE puts(Instruments copy-assignment) return this CE Instrumentsamp operator=(Instrumentsampamp) NE puts(Instruments move-assignment) return this ~Instruments() NE = default non-trivial dtor makes a class non-literal

class MyWidget public Instruments

httptinyccInstrument

The default constructor

Just to give you a taste of this talkrsquos theme

What are some things that could go wrong

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltstdstringgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltintgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

watprogcc In instantiation of void UNIT_TEST_COPY_ASSIGNMENT() [with T = Optionalltintgt]progcc1647 required from hereprogcc1113 error uninitialized const ct [-fpermissive] T const ct ^progcc326 note const struct Optionalltintgt has no user-provided default constructor templateltclass Tgt struct Optional ^progcc55 note constructor is not user-provided because it is explicitly defaulted in the class body Optional() = default ^progcc47 note and the implicitly-defined constructor does not initialize int Optionalltintgtt T t ^ httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

Donrsquot =default your default ctor

Defect Report 253 (July 11 2000) Try not to nerf const Widget cw for your users

If a program calls for the default initialization of an object of a const-qualified type TT shall be a class type with a user-provided default constructormdash Draft Standard N4296 sect 85 [dclinit] para 7

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 2: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The Rule of Zero

Write your classes in a way that you do not need to declaredefine neither a destructor nor a copymove constructor or copymove assignment operator

mdash Peter Sommerlad 2013

httpwikihsrchPeterSommerladfilesMeetingCPP2013_SimpleC++pdf

The Rule of Zero (SRP)Classes that have custom destructors copymove constructors or copymove assignment operators should deal exclusively with ownership

Other classes should not have custom destructors copymove constructors or copymove assignment operators

httpflamingdangerzonecomcxx11rule-of-zerohttpaccuorgindexphpjournals1896

mdash R Martinho Fernandes

The Rule of Six (Howard Hinnant)Know when the compiler is defaulting or deleting special members for you and what defaulted members will do

Always define or delete a special member when the compilerrsquos implicit action is not correct

Give tender loving care to each of the 6 special members even if the result is to let the compiler handle it

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

mdash Howard Hinnant 2014

What are the special member functions

Default constructor Destructor

Copy constructor Copy assignment operator

Move constructor Move assignment operator

NOT special member functions

Other constructors Other assignment operators Member swap() Non-member swap() Comparison operators

NOT special member functions

Other constructors Other assignment operators Member swap() Non-member swap() Comparison operators

Wersquoll talk about these too but not quite yet

Sample code for testingdefine CE constexpr Clang doesnt like this but GCCrsquos okay with itdefine NE noexcept

struct Instruments CE Instruments() NE puts(Instruments default-ctor) CE Instruments(const Instrumentsamp) NE puts(Instruments copy-ctor) CE Instruments(Instrumentsampamp) NE puts(Instruments move-ctor) CE Instrumentsamp operator=(const Instrumentsamp) NE puts(Instruments copy-assignment) return this CE Instrumentsamp operator=(Instrumentsampamp) NE puts(Instruments move-assignment) return this ~Instruments() NE = default non-trivial dtor makes a class non-literal

class MyWidget public Instruments

httptinyccInstrument

The default constructor

Just to give you a taste of this talkrsquos theme

What are some things that could go wrong

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltstdstringgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltintgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

watprogcc In instantiation of void UNIT_TEST_COPY_ASSIGNMENT() [with T = Optionalltintgt]progcc1647 required from hereprogcc1113 error uninitialized const ct [-fpermissive] T const ct ^progcc326 note const struct Optionalltintgt has no user-provided default constructor templateltclass Tgt struct Optional ^progcc55 note constructor is not user-provided because it is explicitly defaulted in the class body Optional() = default ^progcc47 note and the implicitly-defined constructor does not initialize int Optionalltintgtt T t ^ httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

Donrsquot =default your default ctor

Defect Report 253 (July 11 2000) Try not to nerf const Widget cw for your users

If a program calls for the default initialization of an object of a const-qualified type TT shall be a class type with a user-provided default constructormdash Draft Standard N4296 sect 85 [dclinit] para 7

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 3: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The Rule of Zero (SRP)Classes that have custom destructors copymove constructors or copymove assignment operators should deal exclusively with ownership

Other classes should not have custom destructors copymove constructors or copymove assignment operators

httpflamingdangerzonecomcxx11rule-of-zerohttpaccuorgindexphpjournals1896

mdash R Martinho Fernandes

The Rule of Six (Howard Hinnant)Know when the compiler is defaulting or deleting special members for you and what defaulted members will do

Always define or delete a special member when the compilerrsquos implicit action is not correct

Give tender loving care to each of the 6 special members even if the result is to let the compiler handle it

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

mdash Howard Hinnant 2014

What are the special member functions

Default constructor Destructor

Copy constructor Copy assignment operator

Move constructor Move assignment operator

NOT special member functions

Other constructors Other assignment operators Member swap() Non-member swap() Comparison operators

NOT special member functions

Other constructors Other assignment operators Member swap() Non-member swap() Comparison operators

Wersquoll talk about these too but not quite yet

Sample code for testingdefine CE constexpr Clang doesnt like this but GCCrsquos okay with itdefine NE noexcept

struct Instruments CE Instruments() NE puts(Instruments default-ctor) CE Instruments(const Instrumentsamp) NE puts(Instruments copy-ctor) CE Instruments(Instrumentsampamp) NE puts(Instruments move-ctor) CE Instrumentsamp operator=(const Instrumentsamp) NE puts(Instruments copy-assignment) return this CE Instrumentsamp operator=(Instrumentsampamp) NE puts(Instruments move-assignment) return this ~Instruments() NE = default non-trivial dtor makes a class non-literal

class MyWidget public Instruments

httptinyccInstrument

The default constructor

Just to give you a taste of this talkrsquos theme

What are some things that could go wrong

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltstdstringgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltintgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

watprogcc In instantiation of void UNIT_TEST_COPY_ASSIGNMENT() [with T = Optionalltintgt]progcc1647 required from hereprogcc1113 error uninitialized const ct [-fpermissive] T const ct ^progcc326 note const struct Optionalltintgt has no user-provided default constructor templateltclass Tgt struct Optional ^progcc55 note constructor is not user-provided because it is explicitly defaulted in the class body Optional() = default ^progcc47 note and the implicitly-defined constructor does not initialize int Optionalltintgtt T t ^ httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

Donrsquot =default your default ctor

Defect Report 253 (July 11 2000) Try not to nerf const Widget cw for your users

If a program calls for the default initialization of an object of a const-qualified type TT shall be a class type with a user-provided default constructormdash Draft Standard N4296 sect 85 [dclinit] para 7

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 4: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The Rule of Six (Howard Hinnant)Know when the compiler is defaulting or deleting special members for you and what defaulted members will do

Always define or delete a special member when the compilerrsquos implicit action is not correct

Give tender loving care to each of the 6 special members even if the result is to let the compiler handle it

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

mdash Howard Hinnant 2014

What are the special member functions

Default constructor Destructor

Copy constructor Copy assignment operator

Move constructor Move assignment operator

NOT special member functions

Other constructors Other assignment operators Member swap() Non-member swap() Comparison operators

NOT special member functions

Other constructors Other assignment operators Member swap() Non-member swap() Comparison operators

Wersquoll talk about these too but not quite yet

Sample code for testingdefine CE constexpr Clang doesnt like this but GCCrsquos okay with itdefine NE noexcept

struct Instruments CE Instruments() NE puts(Instruments default-ctor) CE Instruments(const Instrumentsamp) NE puts(Instruments copy-ctor) CE Instruments(Instrumentsampamp) NE puts(Instruments move-ctor) CE Instrumentsamp operator=(const Instrumentsamp) NE puts(Instruments copy-assignment) return this CE Instrumentsamp operator=(Instrumentsampamp) NE puts(Instruments move-assignment) return this ~Instruments() NE = default non-trivial dtor makes a class non-literal

class MyWidget public Instruments

httptinyccInstrument

The default constructor

Just to give you a taste of this talkrsquos theme

What are some things that could go wrong

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltstdstringgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltintgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

watprogcc In instantiation of void UNIT_TEST_COPY_ASSIGNMENT() [with T = Optionalltintgt]progcc1647 required from hereprogcc1113 error uninitialized const ct [-fpermissive] T const ct ^progcc326 note const struct Optionalltintgt has no user-provided default constructor templateltclass Tgt struct Optional ^progcc55 note constructor is not user-provided because it is explicitly defaulted in the class body Optional() = default ^progcc47 note and the implicitly-defined constructor does not initialize int Optionalltintgtt T t ^ httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

Donrsquot =default your default ctor

Defect Report 253 (July 11 2000) Try not to nerf const Widget cw for your users

If a program calls for the default initialization of an object of a const-qualified type TT shall be a class type with a user-provided default constructormdash Draft Standard N4296 sect 85 [dclinit] para 7

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 5: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

What are the special member functions

Default constructor Destructor

Copy constructor Copy assignment operator

Move constructor Move assignment operator

NOT special member functions

Other constructors Other assignment operators Member swap() Non-member swap() Comparison operators

NOT special member functions

Other constructors Other assignment operators Member swap() Non-member swap() Comparison operators

Wersquoll talk about these too but not quite yet

Sample code for testingdefine CE constexpr Clang doesnt like this but GCCrsquos okay with itdefine NE noexcept

struct Instruments CE Instruments() NE puts(Instruments default-ctor) CE Instruments(const Instrumentsamp) NE puts(Instruments copy-ctor) CE Instruments(Instrumentsampamp) NE puts(Instruments move-ctor) CE Instrumentsamp operator=(const Instrumentsamp) NE puts(Instruments copy-assignment) return this CE Instrumentsamp operator=(Instrumentsampamp) NE puts(Instruments move-assignment) return this ~Instruments() NE = default non-trivial dtor makes a class non-literal

class MyWidget public Instruments

httptinyccInstrument

The default constructor

Just to give you a taste of this talkrsquos theme

What are some things that could go wrong

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltstdstringgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltintgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

watprogcc In instantiation of void UNIT_TEST_COPY_ASSIGNMENT() [with T = Optionalltintgt]progcc1647 required from hereprogcc1113 error uninitialized const ct [-fpermissive] T const ct ^progcc326 note const struct Optionalltintgt has no user-provided default constructor templateltclass Tgt struct Optional ^progcc55 note constructor is not user-provided because it is explicitly defaulted in the class body Optional() = default ^progcc47 note and the implicitly-defined constructor does not initialize int Optionalltintgtt T t ^ httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

Donrsquot =default your default ctor

Defect Report 253 (July 11 2000) Try not to nerf const Widget cw for your users

If a program calls for the default initialization of an object of a const-qualified type TT shall be a class type with a user-provided default constructormdash Draft Standard N4296 sect 85 [dclinit] para 7

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 6: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

NOT special member functions

Other constructors Other assignment operators Member swap() Non-member swap() Comparison operators

NOT special member functions

Other constructors Other assignment operators Member swap() Non-member swap() Comparison operators

Wersquoll talk about these too but not quite yet

Sample code for testingdefine CE constexpr Clang doesnt like this but GCCrsquos okay with itdefine NE noexcept

struct Instruments CE Instruments() NE puts(Instruments default-ctor) CE Instruments(const Instrumentsamp) NE puts(Instruments copy-ctor) CE Instruments(Instrumentsampamp) NE puts(Instruments move-ctor) CE Instrumentsamp operator=(const Instrumentsamp) NE puts(Instruments copy-assignment) return this CE Instrumentsamp operator=(Instrumentsampamp) NE puts(Instruments move-assignment) return this ~Instruments() NE = default non-trivial dtor makes a class non-literal

class MyWidget public Instruments

httptinyccInstrument

The default constructor

Just to give you a taste of this talkrsquos theme

What are some things that could go wrong

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltstdstringgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltintgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

watprogcc In instantiation of void UNIT_TEST_COPY_ASSIGNMENT() [with T = Optionalltintgt]progcc1647 required from hereprogcc1113 error uninitialized const ct [-fpermissive] T const ct ^progcc326 note const struct Optionalltintgt has no user-provided default constructor templateltclass Tgt struct Optional ^progcc55 note constructor is not user-provided because it is explicitly defaulted in the class body Optional() = default ^progcc47 note and the implicitly-defined constructor does not initialize int Optionalltintgtt T t ^ httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

Donrsquot =default your default ctor

Defect Report 253 (July 11 2000) Try not to nerf const Widget cw for your users

If a program calls for the default initialization of an object of a const-qualified type TT shall be a class type with a user-provided default constructormdash Draft Standard N4296 sect 85 [dclinit] para 7

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 7: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

NOT special member functions

Other constructors Other assignment operators Member swap() Non-member swap() Comparison operators

Wersquoll talk about these too but not quite yet

Sample code for testingdefine CE constexpr Clang doesnt like this but GCCrsquos okay with itdefine NE noexcept

struct Instruments CE Instruments() NE puts(Instruments default-ctor) CE Instruments(const Instrumentsamp) NE puts(Instruments copy-ctor) CE Instruments(Instrumentsampamp) NE puts(Instruments move-ctor) CE Instrumentsamp operator=(const Instrumentsamp) NE puts(Instruments copy-assignment) return this CE Instrumentsamp operator=(Instrumentsampamp) NE puts(Instruments move-assignment) return this ~Instruments() NE = default non-trivial dtor makes a class non-literal

class MyWidget public Instruments

httptinyccInstrument

The default constructor

Just to give you a taste of this talkrsquos theme

What are some things that could go wrong

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltstdstringgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltintgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

watprogcc In instantiation of void UNIT_TEST_COPY_ASSIGNMENT() [with T = Optionalltintgt]progcc1647 required from hereprogcc1113 error uninitialized const ct [-fpermissive] T const ct ^progcc326 note const struct Optionalltintgt has no user-provided default constructor templateltclass Tgt struct Optional ^progcc55 note constructor is not user-provided because it is explicitly defaulted in the class body Optional() = default ^progcc47 note and the implicitly-defined constructor does not initialize int Optionalltintgtt T t ^ httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

Donrsquot =default your default ctor

Defect Report 253 (July 11 2000) Try not to nerf const Widget cw for your users

If a program calls for the default initialization of an object of a const-qualified type TT shall be a class type with a user-provided default constructormdash Draft Standard N4296 sect 85 [dclinit] para 7

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 8: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Sample code for testingdefine CE constexpr Clang doesnt like this but GCCrsquos okay with itdefine NE noexcept

struct Instruments CE Instruments() NE puts(Instruments default-ctor) CE Instruments(const Instrumentsamp) NE puts(Instruments copy-ctor) CE Instruments(Instrumentsampamp) NE puts(Instruments move-ctor) CE Instrumentsamp operator=(const Instrumentsamp) NE puts(Instruments copy-assignment) return this CE Instrumentsamp operator=(Instrumentsampamp) NE puts(Instruments move-assignment) return this ~Instruments() NE = default non-trivial dtor makes a class non-literal

class MyWidget public Instruments

httptinyccInstrument

The default constructor

Just to give you a taste of this talkrsquos theme

What are some things that could go wrong

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltstdstringgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltintgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

watprogcc In instantiation of void UNIT_TEST_COPY_ASSIGNMENT() [with T = Optionalltintgt]progcc1647 required from hereprogcc1113 error uninitialized const ct [-fpermissive] T const ct ^progcc326 note const struct Optionalltintgt has no user-provided default constructor templateltclass Tgt struct Optional ^progcc55 note constructor is not user-provided because it is explicitly defaulted in the class body Optional() = default ^progcc47 note and the implicitly-defined constructor does not initialize int Optionalltintgtt T t ^ httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

Donrsquot =default your default ctor

Defect Report 253 (July 11 2000) Try not to nerf const Widget cw for your users

If a program calls for the default initialization of an object of a const-qualified type TT shall be a class type with a user-provided default constructormdash Draft Standard N4296 sect 85 [dclinit] para 7

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 9: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The default constructor

Just to give you a taste of this talkrsquos theme

What are some things that could go wrong

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltstdstringgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltintgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

watprogcc In instantiation of void UNIT_TEST_COPY_ASSIGNMENT() [with T = Optionalltintgt]progcc1647 required from hereprogcc1113 error uninitialized const ct [-fpermissive] T const ct ^progcc326 note const struct Optionalltintgt has no user-provided default constructor templateltclass Tgt struct Optional ^progcc55 note constructor is not user-provided because it is explicitly defaulted in the class body Optional() = default ^progcc47 note and the implicitly-defined constructor does not initialize int Optionalltintgtt T t ^ httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

Donrsquot =default your default ctor

Defect Report 253 (July 11 2000) Try not to nerf const Widget cw for your users

If a program calls for the default initialization of an object of a const-qualified type TT shall be a class type with a user-provided default constructormdash Draft Standard N4296 sect 85 [dclinit] para 7

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 10: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltstdstringgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltintgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

watprogcc In instantiation of void UNIT_TEST_COPY_ASSIGNMENT() [with T = Optionalltintgt]progcc1647 required from hereprogcc1113 error uninitialized const ct [-fpermissive] T const ct ^progcc326 note const struct Optionalltintgt has no user-provided default constructor templateltclass Tgt struct Optional ^progcc55 note constructor is not user-provided because it is explicitly defaulted in the class body Optional() = default ^progcc47 note and the implicitly-defined constructor does not initialize int Optionalltintgtt T t ^ httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

Donrsquot =default your default ctor

Defect Report 253 (July 11 2000) Try not to nerf const Widget cw for your users

If a program calls for the default initialization of an object of a const-qualified type TT shall be a class type with a user-provided default constructormdash Draft Standard N4296 sect 85 [dclinit] para 7

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 11: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The default constructorinclude ltstringgt

templateltclass Tgt struct Optional T t Optional() = default

templateltclass Tgtvoid UNIT_TEST_COPY_ASSIGNMENT() T t T const ct t = ct check this compiles

int main() UNIT_TEST_COPY_ASSIGNMENTltOptionalltintgtgt()

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

watprogcc In instantiation of void UNIT_TEST_COPY_ASSIGNMENT() [with T = Optionalltintgt]progcc1647 required from hereprogcc1113 error uninitialized const ct [-fpermissive] T const ct ^progcc326 note const struct Optionalltintgt has no user-provided default constructor templateltclass Tgt struct Optional ^progcc55 note constructor is not user-provided because it is explicitly defaulted in the class body Optional() = default ^progcc47 note and the implicitly-defined constructor does not initialize int Optionalltintgtt T t ^ httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

Donrsquot =default your default ctor

Defect Report 253 (July 11 2000) Try not to nerf const Widget cw for your users

If a program calls for the default initialization of an object of a const-qualified type TT shall be a class type with a user-provided default constructormdash Draft Standard N4296 sect 85 [dclinit] para 7

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 12: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

watprogcc In instantiation of void UNIT_TEST_COPY_ASSIGNMENT() [with T = Optionalltintgt]progcc1647 required from hereprogcc1113 error uninitialized const ct [-fpermissive] T const ct ^progcc326 note const struct Optionalltintgt has no user-provided default constructor templateltclass Tgt struct Optional ^progcc55 note constructor is not user-provided because it is explicitly defaulted in the class body Optional() = default ^progcc47 note and the implicitly-defined constructor does not initialize int Optionalltintgtt T t ^ httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

Donrsquot =default your default ctor

Defect Report 253 (July 11 2000) Try not to nerf const Widget cw for your users

If a program calls for the default initialization of an object of a const-qualified type TT shall be a class type with a user-provided default constructormdash Draft Standard N4296 sect 85 [dclinit] para 7

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 13: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Donrsquot =default your default ctor

Defect Report 253 (July 11 2000) Try not to nerf const Widget cw for your users

If a program calls for the default initialization of an object of a const-qualified type TT shall be a class type with a user-provided default constructormdash Draft Standard N4296 sect 85 [dclinit] para 7

httpwwwopen-stdorgjtc1sc22wg21docscwg_activehtml253

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 14: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

What about the destructor

Supplying your own destructor immediately breaks the Rule of Zero in just about every possible way

No implicitly defined move ops

Yes implicitly defined copy ops (but thatrsquos deprecated in C++14)

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 15: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Puzzle What does this printinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) what does this print

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 16: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Our move ops have disappearedinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v ~Widget() assert(vempty())

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 17: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

httpaccuorgcontentconf2014Howard_Hinnant_Accu_2014pdf

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 18: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Do I need a virtual destructorIf A is intended to be used as a base class and if callers should be able to destroy polymorphically then make A~A public and virtual

Otherwise make it protected (and not virtual)

mdash Herb Sutter C++ Coding Standards

httpaccuorgindexphpjournals1896[Sutter1]

BUT

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 19: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Polymorphism and value semanticsdonrsquot play well together

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 20: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Polymorphism + value semanticsclass Dog class FoxTerrier public Dog class DireWolf public Dog

void mixupAtTheDogPark(Dogamp one Dogamp two) stdswap(one two) value semantics right

int main() auto asta = stdmake_sharedltFoxTerriergt() auto nymeria = stdmake_sharedltDireWolfgt() mixupAtTheDogPark(asta nymeria)

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 21: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

It is unspecified whether subobjects representing virtual base classesare assigned more than once bythe implicitly-defined copymoveassignment operator

Not convinced yet

mdash Draft Standard N4296 sect 128 [classcopy] para 28

So donrsquot rely on those assignment operatorsImplement them yourself or just =delete them

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 22: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

From here on we assumeno inheritance hierarchy

is involved

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 23: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() v() the AP Comp Sci approach circa 2001 Widget(const Widgetamp rhs) v(rhsv) Widgetamp operator=(const Widgetamp rhs) v = rhsv return this

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 24: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) what does this print

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 25: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

What about copy operationsinclude Instrumenth httptinyccInstrumentinclude ltvectorgt

struct Widget public Instruments stdvectorltintgt v Widget() = default the modern equivalent Widget(const Widgetamp) = default Widgetamp operator=(const Widgetamp) = default

int main() Widget w w2 w = stdmove(w2) ldquoInstruments copy-assignmentrdquo

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 26: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

What about copy operations Certainly donrsquot implement the defaults yourself

Even explicitly =defaultrsquoing will trash your implicitly defined move ops

Explicitly =deletersquoing will also trash the move ops thatrsquos not how youmake a ldquomove-onlyrdquo class

There are a lot of ways to make a class ldquocopy-onlyrdquo Beware

httpsgroupsgooglecomaisocpporgforummsgstd-proposalsHpxOPebDaMAQ-Y4922iHXsJ

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 27: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The implicit definitions are often correct

The implicitly defined (defaulted) copy constructor will copy-construct all the members

The implicitly defined (defaulted) copy assignment operator will copy-assign all the members

What are some ways that could go wrong

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 28: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Exception-safetystruct Name stdstring first middle last

int main() Name v Jerome K Jerome Name n = ampv try while (true) n = new Name() n-gtfirst == n-gtlast == n = v n-gtfirst == n-gtlast == Jerome catch (stdbad_alloc) assert(n-gtfirst == n-gtlast) right

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 29: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Exception-safetystruct Name stdstring first middle last

Name(const Nameamp rhs) first(rhsfirst) middle(rhsmiddle) last(rhslast) The implicitly defaulted copy constructor is fine but

Nameamp operator=(const Nameamp rhs) first = rhsfirst middle = rhsmiddle What if this assignment throws stdbad_alloc last = rhslast We lack the strong exception guarantee

httpc2comcgiwikiExceptionGuarantee

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 30: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Wersquoll get to the move operations in a moment Name(Nameampamp) = Nameamp operator=(Nameampamp) =

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 31: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 32: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Copy-and-swap idiomstruct Name stdstring first middle last

Name(const Nameamp) = default Name(Nameampamp) = default

Nameamp operator=(const Nameamp rhs) Name temp = rhs copy-construct first (might throw but wonrsquot leak) swap(this temp) swap second (cannot throw) return this

Nameamp operator=(Nameampamp rhs) swap(this rhs) rhs is going to get destroyed anyway return this

What could go wrong

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 33: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b)

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 34: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The funniest way for things to go wrongusing stdswap

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Segmentation fault

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 35: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

What does stdswap do

This is everything N4296 has to say about the semantics of the stdswap template

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 36: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

In practicetemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 37: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The funniest way for things to go wrongusing stdswap Wrong Wersquoll show how to implement swap in a moment

struct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default Nameamp operator=(const Nameamp rhs) Name temp = rhs swap(this temp) return this Nameamp operator=(Nameampamp rhs) swap(this rhs) return this

int main() Name a b a = stdmove(b) Move implemented as swap swap implemented as move

httpmelponorgwandboxpermlinkBUlkY1b12S4tIWXR

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 38: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with temp destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 39: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Let the compiler make the copystruct Name stdstring first middle last Name(const Nameamp) = default Name(Nameampamp) = default

an atypical but valid copy-assignment operator Nameamp operator=(Name temp) swap(this temp) return this

Name X L R()

X = L copy-construct temp from L swap X with temp destroy tempX = stdmove(L) move-construct temp from L swap X with temp destroy tempX = R() move-construct temp from R() swap X with R() destroy temp

httpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 40: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Yes this trick is valid

mdash Draft Standard N4296 sect 128 [classcopy] para 17

A user-declared copy assignment operator Xoperator= is a non-static non-template member function of class X with exactly one parameter of type X Xamp const Xamp volatile Xamp or const volatile Xamp

Irsquom not sold on it yet but itrsquos certainly interestinghttpdefinedbehaviorblogspotcom201109rule-of-3-4-swaphtml

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 41: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

So what do we do about swap Specialize stdswapltWidgetgt Overload stdswap for arguments of type Widget Provide a free function swap located via ADL Provide a member function swap (a la stdvector)

Basically what construct(s) do users expect will work

using stdswap swap(widget1 widget2) 123stdswap(widget1 widget2) 12widget1swap(widget2) 4void (f)(WidgetampWidgetamp) = swap 123

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 42: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Do not intrude on namespace std

namespace std overload this produces undefined behavior void swap(Widgetamp a Widgetamp b)

namespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b) httpwwwgotwcapublicationsmill17htm

The behavior of a C++ program is undefined if it adds declarations or

definitions to namespace std or to a namespace within namespace std

unless otherwise specified A program may add a template specialization for

any standard library template to namespace std only if the declaration

depends on a user-defined type and the specialization meets the standard

library requirements for the original template and is not explicitly prohibited

mdash Draft Standard N4296 sect 176421 [namespacestd] para 1

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 43: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Specializing stdswap a Bad Ideaclass Widgetnamespace std specialization this is allowed but a Bad Idea templateltgt void swap(Widgetamp a Widgetamp b)

templateltclass Tgt class Gadgetnamespace std partial specialization is impossible templateltclass Tgt void swap(GadgetltTgtamp a GadgetltTgtamp b)

httpwwwgotwcapublicationsmill17htm

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 44: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Provide an ADL swapclass Name stdstring first middle last

friend void swap(Nameamp Nameamp)

void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

httpbatchsepostsnon-constant-constant-expressionsfriends

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 45: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Provide an ADL swap (with a wrinkle)class Name stdstring first middle last

This version of the swap function is ldquohiddenrdquo from everyone except ADL friend void swap(Nameamp a Nameamp b) afirstswap(bfirst) amiddleswap(bmiddle) alastswap(blast)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) fails which may be finevoid (f)(NameampNameamp) = swap fails which may be fine

httpbatchsepostsnon-constant-constant-expressionsfriends

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 46: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

ADL swap + member function swapclass Name stdstring first middle last

public void swap(Nameamp b) firstswap(bfirst) middleswap(bmiddle) lastswap(blast) void swap(Nameamp a Nameamp b) aswap(b)

using stdswap swap(widget1 widget2) worksstdswap(widget1 widget2) essentially fails (does 3 moves)widget1swap(widget2) worksvoid (f)(NameampNameamp) = swap works

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 47: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Have you seen code that makes qualified calls to

swap in a template like stdswap( a b )

Congratulations you have probably found a bug

mdash Eric Niebler 2014

httpericnieblercom20141021customization-point-design-in-c11-and-beyond

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 48: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Rule of Zero asplodetemplatelttypename Sequencegtvoid non_uniform_shuffle(Sequenceamp s) for (autoamp elt s) pick a random element and swap with it stdswap(elt s[rand() ssize()]) or use ADL there is no other swap in scope

struct Person stdvectorltstdstringgt names What could possibly go wrong

int main() stdvectorltPersongt vec = Alice Murphy Bob Dobbs Carl Carlson non_uniform_shuffle(vec)

httptinyccrule-of-zero-asplode

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 49: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Rule of Zero asplode g++ progcc -Wall -Wextra -std=c++11 -D_GLIBCXX_DEBUG aout

usrlocalgcc-headincludec++600debugsafe_containerh86error attempt to self move assign

Objects involved in the operationsequence this 0x0x1b90cc8 type = N11__gnu_debug15_Safe_containerINSt7__debug6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaIS8_EEES9_NS_14_Safe_sequenceELb1EEE

Aborted

httptinyccrule-of-zero-asplode

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 50: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp a Tamp b) noexcept() T t(stdmove(a)) a = stdmove(b) b = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 51: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) x = stdmove(t)

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 52: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Self-move and self-swaptemplateltclass Tgtvoid swap(Tamp x Tamp x) noexcept() T t(stdmove(x)) x = stdmove(x) BOOM x = stdmove(t)

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 53: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

std types donrsquot support self-moveEach of the following applies to all arguments to functions defined in the C++ standard library unless

explicitly stated otherwise

mdash If a function argument binds to an rvalue reference parameter the implementation may

assume that this parameter is a unique reference to this argument

[Note If the parameter is a generic parameter of the form Tampamp and an lvalue of type A is bound

the argument binds to an lvalue reference and thus is not covered by the previous sentence]

[Note If a program casts an lvalue to an xvalue while passing that lvalue to a library function (eg

by calling the function with the argument move(x)) the program is effectively asking that

function to treat that lvalue as a temporary The implementation is free to optimize away aliasing

checks which might be needed if the argument was an lvalue]

mdash Draft Standard N4296 sect 17649 [resonarguments] para 13

vectoroperator=(vectorampamp) is a function defined in the C++ standard libraryOne of its parameters is an rvalue reference parameterUh-oh

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 54: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

httpcplusplusgithubioLWGlwg-activehtml2063

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 55: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Exceptions to the rule stdunique_ptr (N4296 sect 208213 [utilsmartptruniqueassign] para 6)

ldquoTransfers ownership as if by calling reset(urelease()) followed by get_deleter() = stdforwardltDgt(uget_deleter())rdquo

stdshared_ptr (N4296 sect 208223 [utilsmartptrsharedassign] para 4)ldquoEquivalent to shared_ptr(stdmove(r))swap(this)rdquo

stdweak_ptr (N4296 sect 208233 [utilsmartptrweakassign] para 4)ldquoEquivalent to weak_ptr(stdmove(r))swap(this)rdquo

stdstring (N4296 sect 2142 [stringcons] para 22)ldquoIf this and str are the same object the member has no effectrdquo

DR 2063 ldquoContradictory requirements for string move assignmentrdquoResolved in Lenexa last week by removing sect 2142 [stringcons] para 22

httpcplusplusgithubioLWGlwg-activehtml2063

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 56: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The default move-assignment operators cannot be trusted

I call this the Rule of At Least One

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 57: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwo

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 58: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The default move-assignment operators cannot be trusted

I call this the Rule of At Least OneTwoFour

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 59: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

ldquoOh but I provide an ADL swaprdquoclass Cat stdvectorltstdstringgt names let the move-assignment operator implicitly default friend void swap(Catamp a Catamp b) if (ampa = ampb) stdswap(a b)

Cat xswap(xx) saved by ADL ha my code is foolproof

x = stdmove(x) ldquoOh I would never write thatrdquostdswap(xx) ldquoNor that I know my stuffrdquo

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 60: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

until one dayinclude Cath

struct BagOfCats Cat first second

BagOfCats implementor omits to write an ADL swap()

BagOfCats bagswap(bagbag) ADL finds stdswapltBagOfCatsgt and everything blows up

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 61: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

What wersquove come up with todayclass Cat stdvectorltstdstringgt names int lives public Cat() Cat(const Catamp) = default Cat(Catampamp) = default Catamp operator=(const Catamp rhs) Cat temp = rhs swap(this temp) return this Catamp operator=(Catampamp rhs) swap(this rhs) return this

void swap(Catamp b) using stdswap swap(names bnames) swap(lives blives) void swap(Catamp a Catamp b) aswap(b)

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 62: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

One slide on constexprGCC rejects constexpr member functions of any class with a non-trivial destructor

Trivialness of destructor is very hard to enforce

The definition of a constexpr function shall satisfy the following constraintsmdash each of its parameter types shall be a literal typeFor a non-template non-defaulted constexpr function if no argument values exist such that an invocation of the function could be an evaluated subexpression of a core constant expression the program is ill-formed no diagnostic required

mdash Draft Standard N4296 sect 715 [dclconstexpr] para 33 and para 5

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 63: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

The one place noexcept mattersstruct BadCat Instruments stdvectorltstdstringgt names BadCat() = default BadCat(const BadCatamp) = default BadCat(BadCatampamp rhs) names(stdmove(rhsnames))

int main() stdvectorltBadCatgt badcats for (int i=0 i lt 100 ++i) badcatsemplace_back() watch for the resize httptinyccInstrument

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 64: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Remarks Causes reallocation if the new size is greater than the old capacity If no reallocation happens all the iterators and references before the insertion point remain valid If an exception is thrown other than by the copy constructor move constructor assignment operator or move assignment operator of T or by any InputIterator operation there are no effects If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructibleltTgtvalue is true there are no effects Otherwise if an exception is thrown by the move constructor of a non-CopyInsertable T the effects are unspecified

mdash Draft Standard N4296 sect 23365 [vectormodifiers] para 1 talking about push_back

mdash Draft Standard N4296 sect 2024 [forward] para 9

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 65: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Questions

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 66: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Bonus material stdcompareclass Cat

void swap(Catamp b) int compare(const Catamp b) const

friend bool operatorlt(const Catamp a const Catamp b) return acompare(b) lt 0 friend bool operatorgt(const Catamp a const Catamp b) return acompare(b) gt 0 friend bool operatorlt=(const Catamp a const Catamp b) return acompare(b) lt= 0 friend bool operatorgt=(const Catamp a const Catamp b) return acompare(b) gt= 0 friend bool operator==(const Catamp a const Catamp b) return acompare(b) == 0 friend bool operator=(const Catamp a const Catamp b) return acompare(b) = 0 void swap(Catamp a Catamp b) aswap(b) int compare(const Catamp a const Catamp b) return acompare(b)

httpwwwclubcccmuedu~ajodisseminateACCU_std_spaceshippdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 67: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget w OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

httpwwwclubcccmuedu~ajodisseminateEMC++-Chapter4-SmartPointerspdf

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 68: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget()

private struct Impl unique_ptrltImplgt pImpl

Widget foo() return Widget() OOPS

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()

Page 69: (plus or minus two) Modern C++ Boilerplate or,ajo/disseminate/C++Now2015.pdf · The Rule of Zero (SRP) Classes that have custom destructors, copy/move constructors or copy/move assignment

Bonus material the PIMPL pitfallltltltWidgethgtgtgt

class Widget public Widget() ~Widget() Widget(Widgetampamp) = private struct Impl unique_ptrltImplgt pImpl

The Rule of Five strikes again

ltltltWidgetcppgtgtgt

struct WidgetImpl

WidgetWidget() pImpl(make_uniqueltImplgt())

Widget~Widget()