Post on 25-Jul-2020
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()