Slide 1

Slide 1 text

Idiomatic C++ Federico Ficarelli http://nazavode.github.io

Slide 2

Slide 2 text

Overview ◼ Chapter 1: Design Issues ◼ Chapter 2: Construction/Destruction/Copying ◼ Chapter 3: Namespaces ◼ Appendix: Tools Federico Ficarelli, Idiomatic C++ 2

Slide 3

Slide 3 text

Bibliography ◼ Herb Sutter and Andrei Alexandrescu, C++ Coding Standards ◼ Bjarne Stroustrup’s C++ Style and Technique FAQ [www.stroustrup.com/bs_faq2.html] ◼ stackoverflow.com, C++faq tag ◼ Microsoft Channel 9 [http://channel9.msdn.com/] ◼ Scott Meyers, Effective C++ ◼ cppreference.com ◼ Bjarne Stroustrup, The C++ Programming Language Federico Ficarelli, Idiomatic C++ 3

Slide 4

Slide 4 text

Chapter 1 Design Issues: Idioms and Best Practices

Slide 5

Slide 5 text

Resource management: the naive approach In order to guarantee resources release, we need to keep track of: ◼ natural return; ◼ return statements; ◼ exceptions thrown. Issues: ◼ code complexity; ◼ duplicated code (copy and paste); ◼ forces to catch and re-throw (even if we aren’t able to handle it); ◼ error prone. void foo () { char* ch = new char[100]; if (...) if (...) return; else if (...) if (...) else throw "ERROR"; // This may not be invoked... delete [] ch; } void bar () { lock.acquire(); if (...) if (...) return; else throw "ERROR"; // This may not be invoked... lock.release(); } Federico Ficarelli, Idiomatic C++ 5

Slide 6

Slide 6 text

Resource Handler Idiom: let the stack do the work template class AutoDelete { public: AutoDelete (T * p = 0) : ptr_(p) {} ~AutoDelete () throw() { delete ptr_; } private: T *ptr_; DISALLOW_COPY_AND_ASSIGN(AutoDelete); }; class ScopedLock { public: ScopedLock (Lock & l) : lock_(l) { lock_.acquire(); } ~ScopedLock () throw () { lock_.release(); } private: Lock& lock_; DISALLOW_COPY_AND_ASSIGN(ScopedLock); }; Exception Safety Rule: the only code that is guaranteed to be executed after an exception is thrown are the destructors of objects residing on the stack. Federico Ficarelli, Idiomatic C++ 6

Slide 7

Slide 7 text

Resource Aquisition Is Initialization (RAII) void foo() { AutoDelete safe_del(new X()); if (...) if (...) return; // No need to call delete here. // Destructor will delete memory } void bar() { ScopedLock safe_lock(l); if (...) if (...) throw "ERROR"; // No need to call release here. // Destructor will release the lock } RAII: every time you need to wrap and manage a resource (memory, file, lock, etc...) in a class, let the constructor acquire and the destructor release it: the stack semantics will release the resource when it leaves the scope. Federico Ficarelli, Idiomatic C++ 7

Slide 8

Slide 8 text

Resource Handler Idiom: file descriptor // Da Stroustrup’s C++ FAQ: class File_handle { FILE* p; public: File_handle(const char* n, const char* a) { p = fopen(n,a); if (p==0) throw errno; } File_handle(FILE* pp) { p = pp; if (p==0) throw errno; } ~File_handle() { fclose(p); } operator FILE*() { return p; } // ... }; void f(const char* fn) { File_handle f(fn,"rw"); // use file through f, // don't care about release // ... } Federico Ficarelli, Idiomatic C++ 8

Slide 9

Slide 9 text

RAII: essential assumptions Single Responsibility Principle: every class should have a single, clear responsibility and that responsibility should be entirely encapsulated by the class. Constructor: it must acquire the managed resource and, in case of failure, raise a proper exception. The acquisition process must be RAII itself (without relying on the destructor). Destructor: it starts with a valid and constructed object (guaranteed by constructor) and must release the resource. It cannot fail. Federico Ficarelli, Idiomatic C++ 9

Slide 10

Slide 10 text

RAII: downsides ◼ Automatic lifetime, tightly bound to stack semantics; ◼ each resource type needs a proper resource holder; ◼ strict ownership, resources cannot be (easily and cleanly) passed around. Federico Ficarelli, Idiomatic C++ 10

Slide 11

Slide 11 text

Ownership: who owns you, baby? How can we explicitly express ownership in C++? Ownership Semantics: an object owns a resource when it has the responsibility to release that resource. struct Resource { void foo() { /* ... */} }; Resource* get_resource() { return new Resource; // Ownership implicitly transferred to the caller } int main (void) { get_resource()->foo(); // ? } // Header file // ... // AMAZING documentation about // the following function. Resource* get_resource(); // ... Federico Ficarelli, Idiomatic C++ 11

Slide 12

Slide 12 text

Ownership semantics and smart pointers std::auto_ptr (C++03) std::unique_ptr (C++11) boost::scoped_ptr (no std) const std::auto_ptr (C++03) std::tr1::scoped_ptr (C++03) boost::shared_ptr (no std) std::tr1::shared_ptr (TR1) std::shared_ptr (C++11) boost::weak_ptr (no std) std::tr1::weak_ptr (TR1) std::weak_ptr (C++11) Strict ownership, transfer allowed Strict ownership, transfer not allowed Shared (multiple) ownership Federico Ficarelli, Idiomatic C++ 12

Slide 13

Slide 13 text

Strict ownership: auto_ptr ◼ Same semantics as raw ptr; ◼ when the owner goes out of scope, raw ptr is destroyed (operator delete); ◼ we can release ownership and take back the raw ptr. Strict Ownership, Transfer Allowed: the auto_ptr has semantics of strict ownership, meaning that there is only one auto_ptr instance responsible for the object's lifetime. If an auto_ptr is copied, the source loses the reference. #include int main() { std::auto_ptr pt( new T ); } // <-- ~T() #include int main() { T* pt1 = new T; // pass ownership to an auto_ptr std::auto_ptr pt2( pt1 ); *pt2 = 12; // same as "*pt1 = 12; pt2->SomeFunc(); // same as "pt1->SomeFunc(); // use get() to see the pointer value assert( pt1 == pt2.get() ); // use release() to take back ownership T* pt3 = pt2.release(); // no owner, no auto-delete! delete pt3; } Federico Ficarelli, Idiomatic C++ 13

Slide 14

Slide 14 text

Strict ownership: auto_ptr int main() { auto_ptr pt( new T(1) ); pt.reset( new T(2) );// ~T(1), owns T(2) } // ~T(2) int main() { auto_ptr pt1( new T ); // pt1 owns auto_ptr pt2; // pt2 non-owning pt1->DoSomething(); // ok pt2 = pt1; // pt1 -> pt2 pt2->DoSomething(); // ok pt1->DoSomething(); // !!! } // ~T() ◼ Ownership can be explicitly dropped and set on the fly (reset); ◼ a non-owning auto_ptr has the same semantics as NULL pointer: never dereference it (check with operator bool). Federico Ficarelli, Idiomatic C++ 14

Slide 15

Slide 15 text

Strict ownership: auto_ptr ◼ No custom deleter, can manage objects allocated with operator new only; ◼ copying and assigning changes the owner of a resource, modifying not only the lhs but also the rhs, which breaks assignment semantics; ◼ cannot be used in stl containers. std::vector< std::auto_ptr > v; /* ... */ std::sort( v.begin(), v.end() ); // ? Federico Ficarelli, Idiomatic C++ 15

Slide 16

Slide 16 text

Strict ownership: scoped_ptr ◼ Used to show explicit ownership; ◼ supports custom deleter; ◼ useful for automatic deletion of local objects or class members (PIMPL, RAII, etc...) ◼ can be “simulated” using the const auto_ptr Idiom. Strict Ownership, Transfer Not Allowed: the scoped_ptr has semantics of strict ownership, meaning that there is only one scoped_ptr instance responsible for the object's lifetime. The owning scoped_ptr cannot be copied, ownership cannot be transferred. const auto_ptr pt1( new T ); auto_ptr pt2( pt1 ); // illegal auto_ptr pt3; pt3 = pt1; // illegal pt1.release(); // illegal pt1.reset( new T ); // illegal Federico Ficarelli, Idiomatic C++ 16

Slide 17

Slide 17 text

Shared ownership: shared_ptr Shared Ownership: the shared_ptr has semantics of multiple ownership, meaning that multiple owning instances are allowed at a time. The instance is reference counted*: it will be destroyed only when the last owner is released. ◼ Useful when object’s lifetime is complex and not tied to a particular scope/object; ◼ supports custom deleter; ◼ can be safely used inside stl containers. Federico Ficarelli, Idiomatic C++ 17

Slide 18

Slide 18 text

Shared ownership: shared_ptr int main() { typedef std::tr1::shared_ptr sh_ptr; sh_ptr p1; { sh_ptr p2(new T()); // ref = 1 p1=p2; // ref = 2 } // ref = 1 } // ref = 0 -> ~T() Reference count semantics template class ArrayDeleter { public: void operator() (T* d) const { delete [] d; } }; std::tr1::shared_ptr array (new double[256], ArrayDeleter()); Custom deleter Federico Ficarelli, Idiomatic C++ 18

Slide 19

Slide 19 text

shared_ptr: Decorator Idiom template class decorator { private: T * p_; public: explicit pointer(T * p): p_(p) {} shared_ptr operator->() const { p_->prefix(); return std::tr1::shared_ptr(p_, std::mem_fn(&T::suffix)); } }; class X { private: void prefix(); void suffix(); friend class decorator; public: void f(); void g(); }; int main() { X x; decorator px(&x); px->f(); px->g(); } Federico Ficarelli, Idiomatic C++ 19

Slide 20

Slide 20 text

Latent ownership: weak_ptr ◼ Expresses «weak» (latent) ownership; ◼ must be casted to shared_ptr before actual use (no operators); ◼ essential to break reference cycles. typedef std::tr1::shared_ptr sh_ptr; struct T { sh_ptr other; }; void test() { sh_ptr p1 (new T()); sh_ptr p2 (new T()); p1->other = p2; // p1 -> p2 p2->other = p1; // p2 -> p1 } // ? typedef std::tr1::shared_ptr sh_ptr; typedef std::tr1::weak_ptr wk_ptr; struct T { wk_ptr other; }; void test() { sh_ptr p1 (new T()); sh_ptr p2 (new T()); if( sh_ptr p = p1->other.lock() ) { p(p2); // p1 -> p2 } if( sh_ptr p = p2->other.lock() ) { p(p1); // p2 -> p1 } } // ? Federico Ficarelli, Idiomatic C++ 20

Slide 21

Slide 21 text

Removing ambiguity, improving robustness ◼ Makes factory functions ownership explicit; ◼ use any type of smart ptr (depending on your needs); ◼ improves robustness dramatically. // Header file // ... // AMAZING documentation about // the following function. std::auto_ptr get_resource(); // ... struct Resource { void foo() { /* ... */} /* ... */ }; std::auto_ptr get_resource() { return std::auto_ptr( new Resource ); // Ownership EXPLICITLY transferred // to the caller } int main (void) { get_resource()->foo(); // ~Resource() } Resource Return Idiom: never return raw pointers from within functions; prefer conveying resource ownership explicitly in the return type. Federico Ficarelli, Idiomatic C++ 21

Slide 22

Slide 22 text

Header files: how make them safe Header files should be written to: ◼ be self-sufficient; ◼ be portable; ◼ minimize dependencies; ◼ avoid pollution of client’s names search space. Federico Ficarelli, Idiomatic C++ 22

Slide 23

Slide 23 text

Header files: (usually) a mess #ifndef _DATE_H_ #define _DATE_H_ #include #include #include using namespace std; namespace calendar { class Date { private: int month_, day_, year_; friend ostream & operator<< (ostream &os, const Date& d); public: Date() { set_date( 1, 1, 1970 ); } Date(int month, int day, int year) { set_date(month, day, year); } // ... void set_date(int month, int day, int year) { month_ = month; day_ = day; year_ = year; } inline void Convert(MuslimDate* other) { // Some heavy work... // from math.h: double res = exp(somevalue); // ... *other = MuslimDate(/*...*/) } inline int get_month() { return month_; } inline get_day() { return day_; } inline get_year() { return year_; } }; ostream & operator<<(ostream &os, const Date& date) { return os << day_ << "/" << month_ << "/" << year_; } } // namespace calendar #endif // _DATE_H_ Federico Ficarelli, Idiomatic C++ 23

Slide 24

Slide 24 text

Header files: pure header and the Inline Guard Idiom // date.h #ifndef IDIOMATICPP_DATE_H_16032013 // <-- #define IDIOMATICPP_DATE_H_16032013 #include // <-- namespace calendar { class MuslimDate; // <-- Forward class Date { private: int month_, day_, year_; friend std::ostream & operator<< (std::ostream &os, const Date& d); public: Date(); Date(int month, int day, int year); void set_date(int month, int day, int year); void Convert(MuslimDate* other); int get_month(); int get_day(); int get_year(); }; // ... // Inline Guard Idiom #if defined(IDIOMATICCPP_USE_INLINE) #define INLINE inline #include #endif } // namespace calendar #endif // IDIOMATICPP_DATE_H_16032013 ◼ Guards: avoid clashes and reserved identifiers; ◼ prefer forward declarations; ◼ remove unused inclusions; ◼ remove using statements; ◼ be sure that the pure header is made of declarations only. Federico Ficarelli, Idiomatic C++ 24

Slide 25

Slide 25 text

Header files: inline-only // date-inl.h #ifndef IDIOMATICPP_DATE_INL_H_16032013 // <-- #define IDIOMATICPP_DATE_INL_H_16032013 #include #include // <-- #include INLINE void Date::Convert(MuslimDate* other) { // Some heavy work... double res = std::exp(somevalue); // <-- // ... *other = MuslimDate(/*...*/) } INLINE int Date::get_month() { return month_; } INLINE int Date::get_day() { return day_; } INLINE int Date::get_year() { return year_; } #endif // IDIOMATICPP_DATE_INL_H_16032013 ◼ Collect all inline definitions in a separate header; ◼ beware of C std headers; ◼ no using statements (qualify all identifiers); ◼ be sure that the inline header is made of inline definitions only. Federico Ficarelli, Idiomatic C++ 25

Slide 26

Slide 26 text

Header files: complete Inline Guard #include #if !defined(IDIOMATICCPP_USE_INLINE) // <-- #define INLINE #include #endif #include using namespace std; // <-- using namespace calendar; Date::Date() { set_date( 1, 1, 1970 ); } Date::Date(int month, int day, int year) { set_date(month, day, year); } void Date::set_date(int month, int day, int year) { month_ = month; day_ = day; year_ = year; } ostream & operator<<(ostream &os, const Date& date) { return os << day_ << "/" << month_ << "/" << year_; } // ...from date.h: // ... // Inline Guard Idiom #if defined(IDIOMATICCPP_USE_INLINE) #define INLINE inline #include #endif // ... ◼ Put the Idiom’s flip side in a single implementation file; ◼ inlining can be controlled by a proper macro definition. Federico Ficarelli, Idiomatic C++ 26

Slide 27

Slide 27 text

Decoupling ◼ The vast majority of header-related idioms care about decoupling; ◼ the main goal is to reduce dependencies and build time; ◼ accepted downsides could impact performance. Federico Ficarelli, Idiomatic C++ 27

Slide 28

Slide 28 text

Decoupling: Interface Class Idiom ◼ Classical OO approach to decoupling; ◼ invokes implementation of an abstraction/class using runtime polymorphism; Dependency Inversion Principle: implementation classes should not depend on each other. Instead, they should depend on common abstraction represented using an interface class. Federico Ficarelli, Idiomatic C++ 28

Slide 29

Slide 29 text

Interface Class Idiom // Abstract base class -> Interface class Exporter { public: virtual std::string toString(Document* doc) = 0; }; // Concrete interface implementors (Liskov) class CSVExporter : public Exporter { public: std::string toString(Document* doc) { /* ... */ } }; class XMLExporter : public Exporter { public: std::string toString(Document* doc) { /* ... */ } }; // Client (Open Close Principle) class ExportController { private: Exporter* m_exporter; // <-- public: void setExporter(Exporter* exporter); void runExport(); }; void ExportController::runExport() { Document* currentDocument = GetCurrentDocument(); // <-- Factory // (no ctors) String exportedString = m_exporter->toString(currentDocument); String exportFilePath = GetSaveFilePath(); WriteStringToFile(exporterString, exportFilePath); } ◼ DIP: ExportController (higher level interface) has no knowledge of any Exporter subclass (lower level interface); ◼ both depend on abstract Exporter interface (the common abstraction). Federico Ficarelli, Idiomatic C++ 29

Slide 30

Slide 30 text

Private Implementation (PIMPL) ◼ The language makes private members inaccessible but not invisible; ◼ idiom meant to completely decouple interface (and clients) from implementation; ◼ implements a true compilation firewall; ◼ consider carefully the advantages (build time, insulation) and downsides (extra indirection level). Federico Ficarelli, Idiomatic C++ 30

Slide 31

Slide 31 text

Private Implementation (PIMPL) // myclass.h: class MyClass { private: struct MyClassPIMPL; // Forward MyClassPIMPL* pimpl_; // Handle public: Handle(); Handle(const Handle&); Handle& operator=(const Handle&); ~Handle(); // Other operations... }; // myclass.cpp: #include "myclass.h" struct MyClass::MyClassPIMPL { int a, b; }; MyClass::MyClass() : pimpl_(new MyClassPIMPL()) { // do nothing } MyClass::MyClass(const MyClass& other) : pimpl_(new MyClassPIMPL(*(other.pimpl_))) { // do nothing } MyClass& MyClass::operator=(const MyClass &other) { delete pimpl_; *pimpl_ = *(other.pimpl_); return *this; } MyClass::~MyClass() { delete pimpl_; } // myclass.h: class MyClass { private: struct MyClassPIMPL; shared_ptr pimpl_; public: // ... }; Federico Ficarelli, Idiomatic C++ 31

Slide 32

Slide 32 text

Functions: avoid making friends ◼ Improves encapsulation by minimizing dependencies: the function cannot depend non- public members (“Don’t give away your internals”); ◼ breaks apart monolithic classes to liberate separable functionalities, reducing coupling. Function Placement: when implementing new functionalities, prefer non-member non-friend functions. When implementing functionalities, the common belief is that OO prefers members. Federico Ficarelli, Idiomatic C++ 32

Slide 33

Slide 33 text

Functions: avoid making friends class NetworkBuffer { public: bool empty() { return this->device->get_packets_count() == 0; } /* ... */ }; class List { public: bool empty() { return this->length == 0; } /* ... */ }; class NetworkBuffer { public: size_t size() { return this->device->get_packets_count(); } /* ... */ }; class List { public: size_t size() { return this->length; } /* ... */ }; template bool empty( const T& s ) { return s.size() == 0; } Federico Ficarelli, Idiomatic C++ 33

Slide 34

Slide 34 text

Functions: avoid making friends if (f needs to be virtual) make f a member function of C; else if (f is operator>> or operator<<) { make f a non-member function; if (f needs access to non-public members of C) make f a friend of C; } else if (f needs type conversions on its left-most argument) { make f a non-member function; if (f needs access to non-public members of C) make f a friend of C; } else if (f can be implemented via C's public interface) make f a non-member function; else make f a member function of C; Functions: the Meyer’s Algorithm [Effective C++] Federico Ficarelli, Idiomatic C++ 34

Slide 35

Slide 35 text

Design guidelines: most wanted 1. Ensure resources are owned by objects. Use explicit RAII and smart pointers to expose ownership and enforce exception safety. 2. Give one entity one cohesive responsibility. 3. Keep your header files clean, don’t harm clients (nor yourself). 4. PIMPL and DIP judiciously. 5. Don’t optimize prematurely: correctness, simplicity and clarity come first. 6. Don’t pessimize prematurely. Federico Ficarelli, Idiomatic C++ 35

Slide 36

Slide 36 text

Chapter 2 Construction/Destruction/Copying

Slide 37

Slide 37 text

Construction/Destruction/Copying Design and implementation of the Big Four: ◼ default construction, ◼ copy construction; ◼ copy assignment, ◼ destruction. Pay attention: ◼ compiler can generate them for you; ◼ the language treats classes with value semantics by deafult. Federico Ficarelli, Idiomatic C++ 37

Slide 38

Slide 38 text

Constructors: let them fail ◼ Throwing from within a constructor is the most safe and widespread technique; ◼ the «init method» technique is unsafe and breaks RAII and all the idioms discussed in this chapter. Failing Constructors: “You should throw an exception from a constructor whenever you cannot properly initialize (construct) an object. There is no really satisfactory alternative to exiting a constructor by a throw”. [Stroustrup’s C++ FAQ] Federico Ficarelli, Idiomatic C++ 38

Slide 39

Slide 39 text

Constructors: let them fail Warning: the constructor itself must be exception-safe (without relying on destructor). class T { public: T(std::size_t len = 0) : array( new int[len] ), buffer( new char[len]) {} // <-- ? // Destructor (omitted) private: int* array; char* buffer; }; class T { public: T(std::size_t len = 0) : array( new int[len] ), buffer( new char[len]) {} // <-- ? // Destructor (omitted) private: std::shared_array array; std::shared_array buffer; }; Federico Ficarelli, Idiomatic C++ 39

Slide 40

Slide 40 text

Resources: the Gold Rule class dumb_string { public: dumb_string(std::size_t size = 0) : // conversion/default mSize(size), mArray(mSize ? new char[mSize]() : 0) {} dumb_string(const dumb_string& other) : // copy mSize(other.mSize), mArray(mSize ? new char[mSize]() : 0) { std::copy(other.mArray, other.mArray + mSize, mArray); } virtual ~dumb_string() { // destructor delete [] mArray; } private: std::size_t mSize; char* mArray; }; int main() { dumb_string a(10); dumb_string b(a); dumb_string c; c = a; // ? } Rule Of Three: if you need to explicitly declare either the destructor, copy constructor or copy assignment operator yourself, you probably need to explicitly declare all three of them. Federico Ficarelli, Idiomatic C++ 40

Slide 41

Slide 41 text

The easy way: non-copyable types #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ void operator=(const TypeName&) // ... private: DISALLOW_COPY_AND_ASSIGN(T); }; template class NonCopyable { protected: NonCopyable () {} ~NonCopyable () {} // Protected non-virtual private: NonCopyable (const NonCopyable &); NonCopyable & operator = (const NonCopyable &); }; class CantCopy : private NonCopyable {}; CRTP Mixin: enables Empty Base Optimization Disable Copying: whenever it makes sense, explicitly disable copy by construction and by assignment. This prevents the language from treating types with unwanted/erratic value semantics. Federico Ficarelli, Idiomatic C++ 41

Slide 42

Slide 42 text

Copy-assignment operator: the tough spot ◼ We need to implement the copy-assignment operator in order to have a correct value semantics; ◼ operator= is much more hard to implement in a robust way than the copy constructor: ▪ must handle an already constructed object; ▪ in case of failure, it must leave the object in the previous consistent state (rollback). The copy-assignment operator must be transactional. Federico Ficarelli, Idiomatic C++ 42

Slide 43

Slide 43 text

operator=: first attempt Issues: ◼ self assignment test: a symptom of non-robust implementation; usually very rare (performance waste); ◼ non exception-safe. dumb_string& operator=(const dumb_string& other) { if (this != &other) // <-- { // Tear down object’s state… delete [] mArray; // <-- mArray = 0; // avoid double-deletion in case of RAII // ...and setup the new one... mSize = other.mSize; // <-- mArray = mSize ? new int[mSize] : 0; std::copy(other.mArray, other.mArray + mSize, mArray); // <-- } return *this; } Federico Ficarelli, Idiomatic C++ 43

Slide 44

Slide 44 text

operator=: exception safety dumb_string& operator=(const dumb_string& other) { if (this != &pOther) // <-- { // setup the new data ready before we teardown the old std::size_t newSize = other.mSize; int* newArray = newSize ? new int[newSize]() : 0; // <-- std::copy(other.mArray, other.mArray + newSize, newArray); // replace the old data (all are nothrow) delete [] mArray; mSize = newSize; mArray = newArray; } return *this; } Issues: ◼ code duplication. Federico Ficarelli, Idiomatic C++ 44

Slide 45

Slide 45 text

operator=: Copy-and-swap Idiom friend void swap(dumb_string& first, dumb_string& second) throw() { std::swap(first.mSize, second.mSize); // throw() std::swap(first.mArray, second.mArray); // throw() } dumb_string& operator=(const dumb_string& other) { dumb_array temp(other); swap(*this, temp); // <-- return *this; } dumb_string& operator=(dumb_string other) // <-- { swap(*this, other); // <-- return *this; } Pass by Value: enables Copy Elision Optimization ◼ Enables strong exception-guarantee (especially the RVO version); ◼ enables type to be used with a large number of idioms. Rule-of-Three-and-a-half: whenever it makes sense, provide a no-fail swap. Federico Ficarelli, Idiomatic C++ 45

Slide 46

Slide 46 text

Transactional programming: essential assumption Foundations Never Fail: everything that destructor, deallocation (e.g.: operator delete) and swap functions attempt shall succeed: never allow an error to be reported from within them. They are the foundation of transactional programming: without their resilience, no-fail rollback is impossible to implement. Federico Ficarelli, Idiomatic C++ 46

Slide 47

Slide 47 text

Copy and destroy consistently When writing a class, consider: ◼ Rule-of-three(and-a-half) to obtain correct and robust value semantics for complex types; ◼ «None-of-three» for POD/aggregates (let the compiler generate them for you); ◼ explicitly disable copy-construction and copy assignment. Federico Ficarelli, Idiomatic C++ 47

Slide 48

Slide 48 text

Chapter 3 Namespaces

Slide 49

Slide 49 text

Keep interfaces coherent ◼ The language is explicitly designed to enforce the Interface Principle, ADL/Koenig Lookup was added for this reason. Interface Principle: for a class T, all functions (including non-member) that both "mention" T and are "supplied with" T in the same namespace are logically part of T, because they form part of T's interface. Federico Ficarelli, Idiomatic C++ 49

Slide 50

Slide 50 text

Argument Dependent/Koenig Name Lookup ◼ Only occurs if the normal lookup of an unqualified name fails to find a matching class member function. “The set of declarations [...] considered for resolution of the function name is the union of the declarations found by normal lookup with the declarations found by looking in the set of namespaces associated with the types of the function arguments”. [C++03, 3.4.2] // using namespace std; std::cout << "hello" << std::endl; Federico Ficarelli, Idiomatic C++ 50

Slide 51

Slide 51 text

Keep interfaces coherent namespace type { class T { public: void f(); }; } namespace ops { T operator+( const T&, const T& ); } int main() { // using ops::operator+; type::T a, b; a.f(); type::T c = ops::operator+(a, b); } namespace ns { class T { public: void f(); }; T operator+( const T&, const T& ); } int main() { ns::T a, b; a.f(); ns::T c = a + b; } Interface Principle, corollary: keep a type and its non-member function interfaces in the same namespace. Federico Ficarelli, Idiomatic C++ 51

Slide 52

Slide 52 text

Keep interfaces clean Depending on std implementation, ADL may choose: ◼ std::operator+ ◼ N::operator+ (pulled in the name search by vector) #include namespace N { struct X {}; template int* operator+( T, unsigned ) { /* do something */ } } int main() { std::vector v(5); v[0]; // <-- v.begin() + 0 } Federico Ficarelli, Idiomatic C++ 52

Slide 53

Slide 53 text

Keep interfaces clean Help prevent name-lookup accidents: protect types from unwanted ADL. «Dual» Interface Principle: avoid putting non-member functions that are not part of the interface of a type T into the same namespace as T, and especially never put templated functions or operators into the same namespace as a user- defined type. Federico Ficarelli, Idiomatic C++ 53

Slide 54

Slide 54 text

Avoid namespace pollution Beware: B::g resolution (and semantics) depends on the headers inclusion order. // f1.h namespace A { int f(double); } // g.h namespace B { using A::f; void g(); } // f2.h namespace A { int f(int); } // g.cpp B::g() { f(1); // <-- ? } Namespace Using Principle: avoid putting using namespace directives before any inclusion directive. Since the inclusion ordering is out of implementor’s control and depends on client’s implementation, never put using namespace directives inside header files. Federico Ficarelli, Idiomatic C++ 54

Slide 55

Slide 55 text

Appendix Tools

Slide 56

Slide 56 text

Static analysis tools ◼ Clang Static Analyzer ◼ Mozilla dehydra ◼ vera++ ◼ cppcheck ◼ Google cpplint Federico Ficarelli, Idiomatic C++ 56

Slide 57

Slide 57 text

Thank you!