modern
play

Modern Modern Template Techniques Template The Simplest Function - PowerPoint PPT Presentation

Modern Modern Template Techniques Template The Simplest Function Template CRTPStatic Polymorphism Techniques Type TraitsBasic Metaprogramming Compile-time Conditionals Policy Classes Perfect Forwarding


  1. Modern Modern Template Techniques • Template The Simplest Function Template • CRTP—Static Polymorphism Techniques • Type Traits—Basic Metaprogramming • Compile-time Conditionals • Policy Classes • Perfect Forwarding • Viewing Deduced Types Jon Kalb Meeting C++ Berlin 2019-11-16

  2. Template Challenge Template Challenge Let’s write a function template from scratch. What is wrong with this? template <class T, class U> Don’t worry it’s the simplest function in the world: add() —it adds two things: T add(T const& a, U const& b) template <class T> { return a + b; } T add(T a, T b) { return a + b; } T is an unknown type so it might be expensive to copy, so: template <class T> T add(T const& a, T const& b) { return a + b; } Easy-peasy, right? The simplest function in the world. Wait, but if we want to add two things of different types? template <class T, class U> T add(T const& a, U const& b) { return a + b; } Meeting C++ Meeting C++ Jon Kalb Jon Kalb 3 2019-11-16 4 2019-11-16

  3. Template Challenge Template Challenge What is wrong with this? What’s wrong with this? template <class T, class U> template <class T, class U> T add(T const& a, U const& b) decltype(T + U) add(T const& a, U const& b) { return a + b; } { return a + b; } What should the return type be? It doesn’t compile! • We can’t assume it is the type of the first parameter. Why not? • We can’t even assume it is the type of either parameter. decltype works on expressions. You can’t add two types. • Adding a char and a short results in an int . How can we fix this? The return type should be whatever you get when you add a T and a U . template <class T, class U> decltype(a + b) add(T const& a, U const& b) How do we say that? { return a + b; } template <class T, class U> decltype(T + U) add(T const& a, U const& b) { return a + b; } Meeting C++ Meeting C++ Jon Kalb Jon Kalb 5 2019-11-16 6 2019-11-16

  4. Template Challenge Template Challenge What’s wrong with this? // C++11 template <class T, class U> template <class T, class U> auto add(T const& a, U const& b) -> decltype(a + b) decltype(a + b) add(T const& a, U const& b) { return a + b; } { return a + b; } It doesn’t compile! This is the simplest template in the world. Why not? But it couldn’t be written in Classic C++ because we need decltype and trailing a and b in decltype(a + b) are not defined. return type function declarations . How can we fix this? Both of these were introduced in C++11 and they are important tools in even template <class T, class U> basic template code. auto add(T const& a, U const& b) -> decltype(a + b) { return a + b; } Meeting C++ Meeting C++ Jon Kalb Jon Kalb 7 2019-11-16 8 2019-11-16

  5. Template Challenge Template Challenge // C++11 // C++14 template <class T, class U> template <class T, class U> auto add(T const& a, U const& b) -> decltype(a + b) auto add(T const& a, U const& b) { return a + b; } { return a + b; } C++14 made it even easier to create templates by allowing us to use type C++14 made it even easier to create templates by allowing us to use type deduction for return types. deduction for return types. Meeting C++ Meeting C++ Jon Kalb Jon Kalb 9 2019-11-16 10 2019-11-16

  6. Modern Template Techniques Modern Template Techniques • • The Simplest Function Template The Simplest Function Template • • CRTP—Static Polymorphism CRTP—Static Polymorphism • • Type Traits—Basic Metaprogramming Type Traits—Basic Metaprogramming • • Compile-time Conditionals Compile-time Conditionals • • Policy Classes Policy Classes • • Perfect Forwarding Perfect Forwarding • • Viewing Deduced Types Viewing Deduced Types

  7. Static Polymorphism CRTP • Polymorphism : • Curiously Recurring Template Pattern: struct derived: base<derived> • a common interface { • defined by a base class ~~~ } • implemented by derived class • Dynamic polymorphism : relies on tools from Object-Oriented Programming. • Does that even compile? • Using a base class pointer (or reference) to a derived class object: • We don’t know actual type at compile time. • Virtual functions • Indirect dispatching at runtime. • Static polymorphism : relies on Curiously Recurring Template Pattern • We know the actual type at compile time • No need for virtual functions or runtime indirection • Allows us to inject behavior into a class without v-table Meeting C++ Meeting C++ Jon Kalb Jon Kalb 13 2019-11-16 14 2019-11-16

  8. CRTP CRTP • Challenge: template <class Derived> struct base • We want to create a base class with an interface that will be implemented { by derived classes, but without virtual functions . void interface() { • We can rely on knowing the type of the derived class at compile time, but // verify pre-conditions, etc we only want to use the inherited interface. static_cast<Derived*>(this)->implementation(); • We want this to type-safe. // verify post-conditions, etc } }; struct derived: base<derived> { void implementation() { /* */ } }; derived d; d.interface(); // Uses the base class interface to get derived behavior. Meeting C++ Meeting C++ Jon Kalb Jon Kalb 15 2019-11-16 16 2019-11-16

  9. CRTP CRTP • The traditional first example from Steve Dewhurst: • Simple example: template <class T> template <class Derived> struct counter struct cloneable { { counter() {++ctr_;} Derived* clone() const counter(counter const&) {++ctr_;} { return new Derived{static_cast<Derived const&>(*this)}; } ~counter() {--ctr_;} }; static long get_count() {return ctr_;} private: struct bar final: cloneable <bar> inline static long ctr_; // inline variables from C++17 { }; bar(int id): id{id} {} int id; struct my_string: counter<my_string> {~~~}; }; my_string a, b{"content"}; my_string c{a}; bar b{42}; bar* my_clone{b.clone()}; std::cout "count: " << my_string::get_count() << "\n"; std::cout << "id: " << my_clone->id << "\n"; count: 3 id: 42 Meeting C++ Meeting C++ Jon Kalb Jon Kalb 17 2019-11-16 18 2019-11-16

  10. CRTP CRTP • More interesting example (thanks to Barton, Nackman, and Dewhurst): template<class T> struct my_complex: eq<my_complex> { template <class T> T real; struct eq T imaginary; { friend bool operator==(T const&a, T const&b) {return a.compare(b) == 0;} bool compare(my_complex const&rhs) const friend bool operator!=(T const&a, T const&b) {return a.compare(b) != 0;} {return (real == rhs.real) and (imaginary == rhs.imaginary);} }; ~~~ }; • Where compare() is defined in the derived class and returns a negative value for less, zero for equals, and a positive value for greater. struct my_string: eq<my_string>, rel<my_string> { template <class T> bool compare(my_string const&rhs) const {return std::strcmp(s, rhs.s);} struct rel ~~~ { friend bool operator<(T const&a, T const&b) {return a.compare(b) < 0;} private: friend bool operator<=(T const&a, T const&b) {return a.compare(b) <= 0;} char* s; friend bool operator>(T const&a, T const&b) {return a.compare(b) > 0;} }; friend bool operator>=(T const&a, T const&b) {return a.compare(b) >= 0;} }; Meeting C++ Meeting C++ Jon Kalb Jon Kalb 19 2019-11-16 20 2019-11-16

  11. CRTP CRTP • Real world example: • Real world example: Solution • In our application, Widgets are always in shared pointers. std::enable_shared_from_this ( a CRTP type) • std::vector<std::shared_ptr<Widget>> // data structure for 
 std::vector<std::shared_ptr<Widget>> // data structure for 
 processedWidgets; // processed Widgets processedWidgets; // processed Widgets struct Widget { 
 struct Widget: std::enable_shared_from_this< Widget > { 
 … 
 … 
 void process() { // Widget-processing function void process() { // Widget-processing function … // process the Widget … // process the Widget processedWidgets.emplace_back(this); // uh oh… processedWidgets.emplace_back(shared_from_this()); } } }; }; • This is a problem waiting to happen. this is a raw pointer, so: • Inherit std::enable_shared_from_this to safely convert this to shared_ptr . • Call to emplace_back creates a control block for *this . • Call to shared_from_this instead of using this . • But there is already at least one std::shared_ptrs pointing to *this . • But how does this work? • So, we have Undefined Behavior. Meeting C++ Meeting C++ Jon Kalb Jon Kalb 21 2019-11-16 22 2019-11-16

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend