Skip to content

Latest commit

 

History

History
3536 lines (3027 loc) · 190 KB

cpp-primer.md

File metadata and controls

3536 lines (3027 loc) · 190 KB

0 Key Words

constexpr:

  • Specifies that the value of a variable or function can apper in constant expression.

    conexpr variable Requirements:

    1. Its type must be a LiteralType.
    2. It must be immediately initialized.
    3. The full-expression of its initialization, including all implicit conversions, constructor calls etc, must be a constant exprssion.
  • conexpr function requirements:

    1. It must not be virtual.
    2. Its return type must be LiteralType
    3. each of its parameters must be LiteralType.

2 Variables and Basic Type

std::function:

  1. Syntax:

    template<Class R, Class....Args>
    class function<R(Args...)>;
  2. Class template std:: function is a general-purpose polymorphic function wrapper. Instances of std::function can store, copy and invoke any Callable target--functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.

  3. Member functions:

    • (constructor), (destructor), operator=, swap, assign(C++17), operator bool, operator()
  4. Target access:

    • target_type: obtain the typeid of the stored target
    • target: obtains a pointer to the stored target
  5. Non-member functions:

    • std::swap, operator==, operator!=
  6. Usage:

    1. store a normal function function<void(int)> func = void (int a) { return ++a;};
    2. store a lambda function<sizt_t(Cumstomer&, Customer&)> func = [](Cumstomer &c, Customer &s) ->size_t{ return 0;};
    3. store the result of a call to std::bind function<void()> func = std::bind(print_num, 3317);
    4. store a call to a member function function<void(const Foo&, int)> func = &Foo::print_add; // get the address of the memeber functioin
    5. store a template function template T g_Minux(T i, T j) { return i - j;} function<int(int, int)> func = g_Minux;
    6. store a function object struct Minux { int operator()(int, int j) { return i - j; } } function<int(int, int)> func = Minux();

Value Category:

  1. glvalue: whose evaluation determine the identity of an object, bit-field, or function;
  2. prvalue: The pure value is the part of the traditional rvalue, is the middle value of the expression, can not take address.
  3. xvalue: demise value is generated by the rvalue. rvalue must be destroyed after the expression has ended. such as return x (x will be destroyed after copy), 1 + 2 (the middle value 3 will be destroyed).
  4. lvalue: a string literal, such as "Hello, world!";
  5. rvalue:

The delctype Specifier:

Syntax:

decltype(entity);
decltype(expression);

Inspects the declared type of the entity or the type and value category of an expression.

  1. If the argument is an unparenthesized id-expression a naming a structed binding, the decltype yields the reference type. (described in the specification of the structed binding declaration). (C++17)
  2. If the argument is an unparenthsized id-expression or an unparenthesized class member access expression, then decltype yields the type of the entity named by this expression. If there is no such entity, or if the argument names a set of overloaded functions, the program is ill-formed.
  3. If the argument is any of other expression of type T, and
    • if the value category of expression is xvalue, then decltype yields T&&;
    • if the value category of expression is lvalue, then decltype yields T&;
    • if the value category of expression is prvalue, then decltype yields T.

If an expression is a function call which returns a prvalue of class type or is a comma expression whose right operand is such a function call, a temporary object is not introduced for materialized from that prvalue.

If the name of an object is parenthesized, it is treated as an ordinary lvalue expression.

Const qualifier:

  1. We can't change the value the of const object after we create it, so it must be initialized.

  2. But we can the const variable via a pointer, the value of the addr pointer pointes is changed, but the const variable value is not changed.

    int a = 10; int *p = (int *)&a; *p = 20; // a is 10, *p is 20
  3. initialize with const:

    • By default, const objects are local to file.
    • The compiler will replaces the uses of the const variable with its value during compile time.
  4. Reference to const: To bound a reference to const we must use reference to const.

    const int i = 12;
    const int &j = i;
    int &k = i; // error: nonconst reference to a const object
  5. initialization and reference to const: There are two exceptions to the rule that the type of the reference must match the type of the object to which it refers:

    • We can initialize the reference to const from the type that can be converted to the type of reference.
      float  a = 3.4;
      const int &b = 3; // ok, b is 3
      int &c = a; // error: invalid initialization of type of reference of type int & from int
    • A reference to const may refer to an object that is nonconst.
  6. pointer and const:

    • pointer to const: the rule is the same as reference.
    • const pointer:
  7. top-lever const:

  8. constexpr and const expression:

    • Wheather a given object is a constant expression depends on the types and initializers.
    • Variables declared as constexpr are implicitly const and must be initialized by const expression.
    • It's good idea to use constexpr for variable that intend to use it as constant expression.
  9. pointer and constexpr:

    • constexpr specifier applies to pointer, not the type to which the pointer points.

Dealing with Types:

  1. auto:
    1. Auto type deduction is template type deduction.
    2. The treatment of braced initializers is the only way where auto type deduction and template type deduction differ.
    3. Auto in a function return type or a labmda parameter implies template type deduction not auto type deduction.
    4. Auto ordinarily ignores the top-level consts.
    5. When use reference, we are really using the object to which it refers.
    6. When we ask for a reference to auto-deduced type, top-level consts in the initializer are not ignored.
    const int ci = i, &cr = i;
    auto b = ci;    // int
    auto c = i;     // int
    auto d = &ci;   // const int * (& of const object is a low-lever const)
    auto e = &i;    // int *       (& of int object is int *)
    auto &h = ci;   // const int &, reference to auto-deduced, consts doesn't ignore
    auto &f = 42;   // error: cannot bind a plain reference to literal
    const auto &j = 42; // ok
  2. delctype:
    decltype((variale)); // always a reference
    decltype(variable);  // reference only when variable is a reference

3: string, vector and array

Array:

  1. Dimension of arrays must be a constant expression.
  2. A default-initialized array have undefined values. Random value given by compiler. eg: int arr[10];
  3. Character arrays can be initialized from a string literal. But string literal end with a null character, so array size must be bigger than the number of explicit elements.
    char arr[5] = "hello";   // error: dimension must be at least 6;
  4. No copy and assignment for arrays initialization.
  5. It’s much more easier to understand array declaration from right to left and from inside to out.
  6. begin() and end() function which are defined in iterator header return pointers to the first and the one past the last element in an array.
  7. The result of subtracting two pointers is a library type named ptrdiff_t (difference_type C++ container library) which is defined in cstddef header file and is a signed integer type.
  8. The result of subtracting two null pointers is 0.
  9. Unlike subscripts for vector and string (Library type such as vector and string that have subscript operator force the index used with subscript to be an unsigned value), the index for built-in subscript operator is not an unsigned value.
  10. To use a multidimensional array in a range for, the loop control variable for all but the innermost array must be references. (As with any array, when we use the name of a multidimensional array, it’s automatically converted to pointer to the first element of that array.)

6: Functions

Overloaded Functions

  1. Default Arguements:
    • If a parameter has default arguement, all the parameter follow it must also have default arguement.
    • A function can be declared mulitple times, but it can only have default specified once in a given scope.
    • Defaults can only be specified if all paramters to the right already have defaults.
  2. Inline and constexpr function:
    • A function defined entirely inside class/struct/union definition, whether it's a member or non-member friend funtion is implicitly an inline function.

    • Inline function avoid function call overhead, it is expanded "in line" at each call.

    • Inline specification is just a request to compiler.

    • Constexpr function restriction:

      • Return type and type of each parameter must be a literal type.
      • Function body must contain exactly one return statement.
    • Inorder to expand function immediately, constexpr functions are implicitly inline.

    • Inline function bans to use loop(for, while), switch, exception interface declaration and recursive function.

7: class

Some classes can’t rely on synthesized default constructor:

  1. Compiler generate default constructor only if we do not define any other constructors.
  2. Synthesized default initialization may do wrong things. Build-in or compound types can be initialized by undefined values.
  3. Compiler is unable to synthesize one default constructor. If a class has a member that has class type which doesn’t have a default constructor, so the compiler can’t initialize that member.

Benefits of Encapsulation:

  1. User code cannot inadvertently corrupt the state of an encapsulated object.
  2. The implementation of an encapsulated object can change over time without requiring changes in user-level code.

Additional Class features

class members revisited

  1. Defining a type member

    typedef std::string::size_type pos; // typedef
    using pos = std::string::size_type; // type alias
  2. Making Members inline

    • It’s legal to specify inline on both the declaration and definition that both inside or outside the class body.
  3. Overloading Functions

    • Overloading functions differ by numbers and/or types of parameters
  4. Mutable Data Member

    • A mutable data member is never const, even it’s a member of a const object, we can modify a mutable data member inside a const function.
  5. Initializers for Data Member of Class Type

    • In-class initializer followed by an = sign or curly brace

Functions That Return *this

  1. A const member function returns *this as a reference to const
    Screen &display (ostream &os)  {do_display(os); return *this; }
    const Screen &display (ostream &os) {do_display(os); return *this; }
    void do_display(ostream &os) {cout << contents << endl; }
  2. When non-const version of display call do_display, it’s this pointer is implicitly converted from a pointer to non-const to a pointer to const
  3. When we call display on an object, whether the object is const determines which version of display is called

Friendship Revisited

Friendship Between Classes

  • The members of friend class can access all members of class granting friendship

Making A Member Function a Friend

Overloaded Functions and Friendship

Friend Declarations and Scope

  1. Class and nonmember functions need not have been declared before they are used in a friend declaration.
  2. When a name is first appears in a friend declaration, it’s implicitly assumed to be part of surrounding scope.
    • However, the friend is not actually declared in that scope.
    • Even if we define the function inside the class, we must provide declaration out the class itself to make that function visible.
struct X {
    friend void f() { return; } // friend function defined inside class
    x() {f();}                  // error: f is not declared
    void g();
    void h();
};

void X::g() { f(); }    // error: f hasn’t been declared
void f();               // declare f function
void X::h() { f(); }    // ok: declaration of f is now in scope

Class Scope

Scope and Member Defined outside the Class

  1. Once the class name is seen, the remainder of class including the parameter list and function body is in scope of the class
  2. If a member function is defined outside the class, any name used in return type is out the scope of class.
    • As a result, we must specify the class of which it is a member.
    // ScreenIndex which is a type name defined inside Window_mgr class
    Window_mgr::ScreenIndex Window_mgr::addScreen(const Screen& s) {
    
    }

Name Lookup and Scope

  1. Class definition are processed in two phases:
    • Members declaration are processed first
    • Members body are processed after entire class has been seen
  2. Name Lookup for Class Member Declaration
    • Name used in declaration, including names used for return type and types in parameter list, must see before they are used.
  3. Type Name Are Special
    • An inner scope can redefine a name from outer scope, but if that name is a type, the class may not subsequently redefine that name.
  4. Normal Block-Scope Name Lookup inside Member Function
    • It’s a bad idea to use same name for a parameter and a member
  5. After Class Scope, Look in the Surrounding Scope
    • Names Are Resolved Where They Appear Within a File

Constructor Revisited

7.5.1 Constructor Initializer List

  1. Constructor Initializers Sometimes Are Required

    • We must use constructor initializer list to provide values for members that are const or references or that are of a Class type that does not define a default constructor
  2. Order of Members Initialization

    • A constructor that supplies default arguments for all its parameters also defines the default constructor
    FOO(string name = "XXX", string sex = "man", int age = 20) : name(name), sex(sex), age(age) {}

7.5.2 Delegating Constructor

Sales_data(): Sales_data("", 0, 0);
Sales_data(string s): Sales_data(s, 0, 0);

7.5.3 The Role of Default Constructor

  1. Default initialization happens:
    1. When we define non-static variables or arrays at block scope without initializer
    2. When a class has members of class type uses synthesized default constructors
    3. When a class member are not explicitly initialized in a constructor initializer list
  2. Value initialization happens:
    1. During array initialization we provide fewer initializer than the size of array
    2. When we define a local static object without initializer
    3. When we explicitly request value initialization by writing the expression of the form of T(), where T is the name of a type

7.5.4 Implicit Class-type Conversion

  1. One class-type conversion is allowed
    // item.combine(Sales_data sd);
    // error: "999-999" convert to string then convert to Sales_data
    item.combine("999-999");
    // ok
    item.combine( (string)"999-999" );
    item.combine( (Sales_data)"999-999" );
  2. Class-type conversions Are Not Always Useful
    • Suppressing Implicit Conversions Defined By Constructor
    • We can suppress implicit conversions defined by constructor by declaring the constructor as explicit
      explicit Sales_data( string &s) : book(s) { };
      explicit Sales_data( istream &is);
  3. Explicit can only be used in declarations of constructors and conversion operations which constructor can be called by only one simple argument.
  4. Explicit Constructor Can Be Used Only for Direct Initialization

7.5.5 Aggregate Classes

7.5.6 Literal Classes

7.6 static Class Members

  1. Can not declare a static member function as const and refer this in the function body
  2. Static members can be defined inside or outside the class body. The static keyword is used only on the declaration inside the class body not on the definition outside the class body.
  3. Static members should be initialized outside the class body. We can provide in-class initializers for static members that have const integer type and must do so for static members that are constexpr of literal type. If there is an initializer inside the class there shouldn’t be another one outside the class.
  4. Even if a const static data member is initialized in the class body, the members ordinarily should be defined outside the class definition.

8: IO Library

8.1 The IO Class

8.1.1 No copy or assign for IO Object

8.1.2 Condition States

  1. IO type: ios_base::iostate
  2. State flag:
    • ios_base::goodbit
    • ios_base::badbit
    • ios_base::eofbit
    • ios_base::failbit
  3. Functions: s.good() s.bad() s.eof() s.fail() s.clear() s.clear(flag) s.rdstate()
  4. Once an error has accoured, subsequent IO operations on that stream will fail
  5. Interrogate the State of Stream
    • Fail returns true if bad is set.
  6. Flushing the Output Buffer
    • Flush: flush the buffer but add no data
    • Endl: end the current line and flush the buffer
    • Ends: add null character and flush the buffer
    • Buffers are not flushed if the program crashes
  7. Typing Input And Output Stream Together
    • When an input stream is tied to an output stream, any attempt to read the input stream will first flush the buffer associated with output stream

8.2 File Input and Output

  1. The Open and Close Members
    • Once a file is opened, the file stream remains associate the file.
    • Open an opened file will fail and subsequent operations will also fail and set failbit
    • Once the file is closed, we can open a new file.
  2. It’s good practice to verify the open succeeded.
  3. When a file stream object goes out the scope, the file it is bound is automatically closed.
  4. File Mode
    • fstream::in out app(seek to the end before every write)
    • ate(seek to the end immediately af open)
    • fstream::binary trunc(truncate the file)
  5. The only way to preserve existing date in a file opened by an ofstream is specify app or in mode explicitly.

12 Dynamic Memory

  1. Common operation on shared_ptr and unique_prt

    shared_prt<T> p; p->get(); p->man(); swap(m, n); unique_ptr<T> q;
  2. Operation specified to shared_ptr

    make_shared<T>(args);   shared_ptr<T> p(q);     p = q;      p.unique(); p.use_count();
    shared_ptr<T> p(q)      shared_ptr<T> p(q, deleter)         shared_ptr<T> p(u)
    shared_ptr<T> p(p2, d)  p.reset()               p.reset(q)  p.reset(q, d)
  3. Operation specified to unique_ptr

    unique_ptr<T, D> u(q);  u.release()             u.reset(q)
  4. Operation specified to weak_ptr

     weak_ptr<T> w;          weak_ptr<T> w(sp);      w = p;      w.reset();
     w.use_count();          w.expired();            w.lock();
  5. Programs tend to use dynamic memory for three purpose:

    1. They don’t know how many objects they need
    2. They don’t know the precise type of the objects they need
    3. They want to share data between several objects
  6. If new is unable to allocate the request storage, it’ll throw an exception of type bad_alloc, we can use nothrow keyword to prevent it throw such exception, the result is a null pointer.

  7. Smart pointer must be initialized directly, we can’t implicitly convert a built-in pointer to smart pointer

    std::shared_ptr<int> p = new int(1024);  // error: implicit conversion
    std::shared_prt<int> p(new int (1024));      // ok: user direct initialization
  8. Don’t mix ordinary pointer and smart pointer

    • It’s dangerous to use built-in pointer to access the object owned by smart pointer, because we may not know when the object is destroyed.
  9. Don’t use get to initialize or assign another smart pointer

  10. Use get() to pass access to the pointer to the code that will not delete the pointer.

  11. Smart pointer can automatically free the memory it manages when an exception occurs

  12. Smart pointer pitfalls:

    1. Don’t use built-in pointer to initialize or set more than one smart pointer
    2. Don’t use get() to initialize or assign smart pointer.
    3. Pointer returned by get() will become invalid when last corresponding sp goes away.
    4. Don’t delete a pointer returned by get().
    5. If use sp manage resource other than memory allocated by new, pass a deleter.
    6. Passing and returning unique_ptr
  13. There is one exception for the rule that we cannot copy or assign a unique_ptr:

  • we do those when a unique_ptr is about to be destroyed.
  1. Dynamic Arrays
    1. We can use empty parentheses to value initialize elements of a dynamic array but not the initializer inside the parentheses which means that we cannot use auto to allocate an array.
    2. It’s legal to create an array variable of size 0
      deleter [] pa; // delete a dynamic array.
    3. Shared_ptr does not directly support dynamic array, so:
      1. We must define our own deleter.
      2. Smart pointers do not support pointer arithmetic and subscript, to access the elements of a dynamic array we can use get() to obtain a built-in pointer.
        for (size_t i = 0; i < 10; i++)  {
            // get() obtains a normal pointer which support pointer arithmetic.
            *(sp.get() + i) = i;
        }
  2. The Allocator Class
    1. Decoupling the allocation from construction.
    2. Operation:
      std::allocator<T> a;
      a.allocate(n);          a.deallocate(p, n);
      a.construct(p, args);   a.destroy(p);
    3. Algorithms to copy and fill the uninitialized memory
      // returns a pointer pointing to the element pass to the last constructed element
      uninitialized_copy(b, e, p2); uninitialized_copy_n(b, n, p2);
      uninitialized_fill(b, e, t);   uninitialized_fill_n(b, n, t);

13: Copy Control

Copy, Assign and Destroy

The Copy constructor

  1. A constructor is a copy constructor if its first parameter is a reference to the class type, and any additional parameters have default values.

  2. If we do not define copy constructor the compiler will synthesize one for us even if we define any other constructor.

  3. Difference between copy and direct initialization:

    1. Direct: we’re asking compiler use ordinary function matching to select the constructor that best matches the arguments we provide;
    2. Cope: we’re asking compiler to copy the right-hand operand to the object being created, converting if necessary.
  4. Copy constructor happens:

    1. When we use =
    2. Pass an object as an argument to parameter of non-reference type
    3. Return an object from a function that has non-reference return type
    4. Brace initialize the elements of an array or elements of aggregate class
  5. Why Copy construct first parameter must be a reference:

    • To call copy constructor, we’d need to call copy constructor to copy the arguments, to copy the arguments, we’d need to call copy constructor and indefinitely.
  6. C.67: A polymorphic class should suppress copying

Copy-assignment operator:

  1. Assignment operators ordinarily should return a reference to their left-hand operand.

  2. Compilers will generate synthesized copy-assignment operator if we don’t define it’s own.

  3. Some synthesized copy-assignment operator disallow assignment, otherwise, it assigns each right-hand object to the corresponding left-hand object of the class using copy-assignment operator for the type of that member.

  4. Copy elision:

    1. In a return statement, when the oparand is a prvalue as the same class type as the return type

      T f() {
          return T(); // f(), only one call to default constructor of T
      }
    2. In a initialization of a variable, when the initializer expression is a prvalue of the same type as the variable type

      T x = T(T(f()));    // only one call to default constructor of T to initialize x
    3. In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and which is of the same class type (ignoring cv-qualification) as the function return type. This variant of copy elision is known as NRVO(named return value optimization).

    4. In the initialization of an object, when the source object is a nameless temporary and is of the same class type (ignoring cv-qualification) as the target object. When the nameless temporary is the operand of a return statement, this variant of copy elision is known as RVO(return value optimization).

    5. In a throw-expression, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and whose scope does not extend past the innermost try-block (if there is a try-block).

    6. In a catch clause, when the argument is of the same type (ignoring cv-qualification) as the exception object thrown, the copy of the exception object is omitted and the body of the catch clause accesses the exception object directly, as if caught by reference (there cannot be a move from the exception object because it is always an lvalue). This is disabled if such copy elision would change the observable behavior of the program for any reason other than skipping the copy constructor and the destructor of the catch clause argument (for example, if the catch clause argument is modified, and the exception object is rethrown with throw). (sicen C++11)

  5. If a class needs a destructor, it’s almost surely it needs copy constructor and copy-assign operator.

  6. Classes that need copy need assignment, and vice versa.

  7. Preventing Copy: =delete

    • =delete must appear on the first declaration of the deleted function.
    • We can specify =delete on any function.
  8. The destructor should not be deleted

  • The compiler won’t let us define variables or temporaries of type that has deleted destructor.
  1. The Copy-control members may be synthesized as deleted

    1. Deleted destructor: the class has member whose own destructor is deleted or inaccessible.
    2. Deleted copy constructor: class has member whose own copy constructor is deleted or inaccessible or has member with deleted or inaccessible destructor.
    3. Deleted copy-assignment operator: if a member has deleted or inaccessible copy-assignment operator, or the class has const or reference member.
    4. Deleted default constructor: if the class has deleted or inaccessible constructor; or a reference member does not have an in-class initializer; or a const member whose type does not explicitly define a default constructor and that member does not have in-class initializer.
  2. Two points in mind when writing assignment operator:

    1. Assignment operator should work correctly if an object is assigned to itself
    2. Most assignment operators share work with destructor and copy constructor
  3. Good pattern to write assignment operator:

    1. Copy the right-hand operand into the local temporary
    2. Destroy the existing members of the left-hand operand
    3. Copy the data from temporary into the members of the left-hand operand
      HasPtr & operator= (HasPtr &rhs) {
          string p = new string(*rh.ps);  //  first step
          delete ps;                      //  second step
          this->ps = p;                   //  third step
          this->num = rhs.num;
          return *this;
      }
      
      HasPtr& operator=(HasPtr& rhs) {
          if (this != &rhs) {
              delete this->ps;
              this->ps = string(*rhs.ps);
              this->num = rhs.num;
          }
      
          return *this;
      }
  4. lvalue/rvalue reference:

    1. Rvalue reference may be bound only to an object that is about to be destroyed.

    2. Lvalue reference refers to an object's identity whereas rvalue refers to an object's value.

    3. Functions that return lvalue references, along with assignment, subscript, dereference and prefix increment/decrement operators, are all examples of expression that return lvalue.

    4. Functions that return rvalue reference, along with arithmetic, relational, bitwise, postfix increments/decrement operators, are all yield rvalue.

      1. We can bind either lvalue to const or rvalue to these expressions, and its lifetime is extended.
        int &&x = 2 + 3; // x itself a lvalue, but it binds to a rvalue
        const int& x = 2 + 3;
      2. Const reference can accept both lvaue reference and rvalue reference.
      3. Rvalue bind to a const reference, it's lifetime is extended.
      4. Named rvalue is a lvalue: the variable bind to rvalue itself is lvalue.
    5. Because rvalue reference can only be bound to temporaries, so:

      1. There can be no other users of that object
      2. These facts together mean that code uses rvalue reference is free to take over the resource from the object which the reference refers.
      3. The referred-to object is about to be destroyed
    6. Rvalue references refer to objects that are about to be destroyed. Hence, we can "steal" the state from an object bound to rvalue reference.

    7. A variable is an lvalue; we cannot bind an rvalue reference to an variable even if that variable is defined as an rvalue reference type.

  5. std::move

    1. We can destroy the moved-from object and assign a new value to it, but cannot use the value of moved-from object.
    2. Move constructor and move assignment operator that cannot throw exceptions should be marked as noexcept.
    3. Unless the library knows that our move constructor can't throw, it will do extra work to cater to the possibility that moving an object of our class type might throw.
    4. Move operation doesn't throw because two interrelated facts:
      • first, although, move operations usually don't throw exceptions, they are permitted to so.
      • Second, the library provide guarantees to what they do if an exception happens.
    5. After move operation, the moved-from object must remain a valid, destructible object but user may make no assumptions about its value.
    6. The compiler synthesizes the move constructor and move assignment operator only if the class doesn't define any of its copy-control members and only if all data members can be moved constructed and moved assigned, respectively.
    7. Move operations are never implicit defined as deleted.
    8. Move members are defined as deleted:
      1. if the class has a member that defines its own copy members but move members or if the class has a member that doesn't define its own copy members for which compiler is unable to synthesize move members.
      2. if the class has a member whose move members are deleted or inaccessible.
      3. if the destructor of the class is defined as deleted or inaccessible
      4. if the class has members that are const or reference member.
      5. if ask compiler to generate a move operation by using =default, then the copy operation will be defined as deleted.
      6. Declaring a destructor has a potentially significant side effect: it prevents the move operations from being generated.
    9. Class has define its own move constructor or move-assignment operator must define its own copy operations. Otherwise, those members are deleted as default.
    10. Lvalues are moved, lvalues are copied but r values are copied when there is no move constructor.
  6. Move Iterator:

  • Return rvalue reference by calling library make_move_iterator function which takes a normal iterator and returns a rvalue reference rather than normal iterator which return lvalue reference.
  1. Rvalue LValue reference member function:

    • In order to maintain backward compatibility, the library continue allow assignment to rvalue. However, we can prevent this feature by indicating rvlaue/lvalue reference in the same way that we define const member functions, we place a reference qualifier (& or &&) after the parameter list.
    • A function can be both const and reference qualified, the reference must follow const.
  2. Overloading and Reference Function

    • If a member function has a reference qualifier, all the versions of that function with same parameter list must have reference qualifier(overloading functions have ref-qualifier).
    void func() const &;
    void func() const &&;

14 Overloaded Operator and Conversion

  1. When an overloaded operator is a member function, this is bound to the left-hand operand. Member operator functions have one less (explicit) parameter than the number of operands.
  2. An operator function must either be a member of a class or have at least one parameter of class type.
  3. Ordinarily, the comma, address-of, logical AND, and logical OR operators should not be overloaded.

Overloading the output operator<<

  1. Ordinarily, the first parameter of output is a non const ostream reference, non const because writing to the stream can change it state, reference because we can't copy ostream object. The second parameter should be a const reference to class type.
    ostream& operator<<(ostream& os, const T& t);
  2. To be consistent with other output operator, operator<< returns a ostream reference.
  3. Output operator should print the contents of the object with mininal formatting, they shouldn't print a newline.
  4. Io operators mube be nonmember functions and usually be declared as friends.

Overloading the input operator>>

Input operators must deal with possibility that input might faill. Output operators generally don't bother.

Input operator should decide what, if anything, to do about error recovery.

Arithmetic and Relational operator

Ordinary, the parameter of arithmetic and relational operator should be const. An arithmetic generates a new value which is the result of computation on its two params

Classes that define both arithmetic operator and related compound assignment operator ought to implement the arithmetic operation by using the compound assignment.

Relational Operator

If a single logical definition for < exists, classes usually should define the < operator. However, if the class also has ==, define < only if the definitions of < and == yield consistent results.

Assignment operator

Assignment operators must, and ordinarily compound-assignment operators should, be defined as members.

These operators should return a reference to the left-hand operand.

Subscript Operator

If a class has a subscript operator, it usually should define two versions:

  1. one that returns a plain reference
  2. the other that is a const member and returns a reference to const.

Increment and Decrement Operator

To be consistent with the built-in operators, the postfix operators should return the old (unincremented or undecremented) value. The rvalue is returned as a value, not a reference. But the prefix operators return reference.

p.operator++(0);  // call postfix operator
++p.operator++(); // call prefix operator++

Function-call operator

The function-call operator must be a member function.

A class may define multiple versions of the call operator, each of which must differ as to the number or types of their parameters.

Function objects are often used in combination with the standard algorithme.

Lambdas Are Function Objects

When we write a lambda, the compiler translates that expression into an unnamed object of an unnamed class which contain an overloaded function-call operator which is const function but we can use 'mutable' key word to modify the captured data members.

Classes generated from lambda expression have a deleted default constructor deleted assignment operator, and a default destructor, where has copy/move constructor depends on the type of the captured data members.

Callable Objects:

  1. functions
  2. pointer to function
  3. lambda
  4. objects created by bind
  5. classes that overloaded function-call operator

Call signature

specifies the type returned by a call to the object and the argument type that must be passed in the call.

The Library function Type (P 577)

  • Defined in functional header, represent callable objects with return type and parameter type.
  • We cannot directly store the name of an overloaded function in an object of type function.
  • function<int(int, int)> f2 = [](int i, int j) { return i % j; };

function:

  • function f; function f(nullptr); function f(obj); f(args); result_type;
  • deprecated C++17: argument_type; first_argument_type; second_argument_type;

Conversion Operator

explicit operator type() const;

A conversion function must be a member function, may not specify return type, and must have an empty parameter list, and is called automatically. The function usually should be const.

Conversion operators are applied implicitly that can yield surprising results, we can prevent this by using explicit keyword.

explicit operator int() const { return val;}    // define explicit conversion
static_cast<int>(obj);                          // use explicit conversion

Explicit conversion can implicitly use as:

  • The condition of an if, while, do statement
  • The condition expression in a for, logical NOT(!), OR(||), AND(&&), (?:).

new and delete:

Category:

  • new/delete/new[]/delete[] operator // global, can not be overloaded
  • operator new/delete/new[]/delete[]
  • placement new/new[] // note: there are no placement delete/delete[]

Process of new and delete:

  • new: call operator new to malloc memory, call constructor construct object and return pointer
  • delete: call destructor destruct object, free memory

15 Object-Oriented Programming

  • what makes virtual calls slower 🔗 Ref
    • Extra indirection (pointer dereference) for each call to a virtual method.
    • Virtual methods usually can’t be inlined, which may be a significant cost hit for some small methods.
    • Additional pointer per object. On 64-bit systems which are prevalent these days, this is 8 bytes per object. For small objects that carry little data this may be a serious overhead.
      • This can definitely play a role in some scenarios (i.e. a lot of small objects where the additional memory means less of them fit into L1 data cache).

16 Template

  1. Template arguments used for non type template parameters must be constant expression.
  2. Template programs should try to minimize the number of arguments placed on the arg types.
  3. Function templates and member functions of class templates are ordinarily put into head file.
  4. It's up to the caller to guarantee that the arguments passed to the template support any operations that template uses, and that those operations behave correctly in the context in which the template uses them.

Defining a Template

Function Templates

template <typename T>
int compare(const T &v1, const T &v2)
{
    if (v2 < v1) return 1;
    if (v1 < v2) return -1;
    return 0;
}
  1. Instantiating a Function Template
  2. Template Type Parameters
  3. Nontype Template Parameters
  4. inline and constexpr Function Templates
  5. Writing Type-Independent Code

Class Template

  1. A function of a class template is instantiated only if the program uses that function.
  2. There is one exception to the rule that when we use a class template we must supply the template arguments that is in the scope of template itself.
  3. Class and it's friends can both be template or not. A class that has non-template friends grants that friends access or instantiation of the template. When friends is itself a template, the class granting friendship controls friendship includes all instantiations or specified instantiation.
  4. In the scope of the class template, we may refer to the template without specify the template arguments.
  5. In order to refer to a specific instantiation of a template, we must declare first the template itself.
template<typename> class Friend;
template<typename> class Base;
template<typename T> bool compare(const Base<T>&, const Base<T>&);

template<typename T>
class Base {
public:
    friend bool compare<T>(const Base<T>&, const Base<T>&);

    /* one-to-one friendShip
     * Forward declaration necessary */
    friend class Friend<T>;

    /* Specific friendShip
     * Forward declaration necessary */
    friend class Friend<int>;

    /* Generic friendShip
     * Forward declaration unnecessary
     * All instantiations are friends to each instantiation of Base */
    template<typename T> friend class Friend;
};
  1. References to a Template Type in the Scope of the Template

  2. Member Functions of Class Templates

  3. The check and Element Access Members

  4. Blob Constructors

  5. Instantiation of Class-Template Member Functions

  6. Simplifying Use of a Template Class Name inside Class Code

  7. Using a Class Template Name outside the Class Template Body

  8. Class Templates and Friends

  9. One-to-One Friendship

  10. General and Specific Template Friendship

  11. Befriending the template's own type parameter:

    template<typename Type> class Bar {
        // if Type is class type Base, so Base is friend of Bar
        friend Type;                 // grants access to Type used to instantiate the Bar
    }

    Whatever Type is a class or build-in type it works good.

  12. Template Alias

    Because template is not a type, we cannot define a typedef that refers to a template. But using:

    // Syntax:
    using identifier attr(optional) = type-id ;
    template <template-parameter-list >
    using identifier attr(optional) = type-id ;
  13. static Members of Class Templates

    template<typename T>
    class Foo {
    public:
        static T count(T a) {
            return 10;
        }
    
        static string count() {
            return "yes";
        }
    };
    
    Foo<string> fo1;
    Foo<int> fo2;
    cout << fo1.count() << endl;
    cout << fo2.count(5) << endl;
    cout << Foo<int>::count() << endl;

Attribute specifier sequence(C++11)

[[attr]] [[attr1, attr2, attr3(args)]] [[namespace::attr(args)]] alignas_specifier Formally, the syntax is: [[ attribute-list ]] (C++11) [[ using attribute-namespace : attribute-list ]] (C++17)

where attribute-list is a comma-separated sequence of zero or more attributes (possibly ending with an ellipsis ... indicating a pack expansion) identifier identifier ( argument-list ) attribute-namespace::identifier attribute-namespace::identifier ( argument-list )

  1. simple attribute, such as [[noreturn]]
  2. attribute with arguments, such as [[deprecated("because")]]
  3. attribute with a namespace, such as [[gnu::unused]]
  4. attribute with both a namespace and an argument list

If using: namespace appears in the beginning of an attribute list, no other attributes in the attribute list can specify a namespace: the namespace specified in a using applies to them all:

  • [[using CC: opt(1), debug]] // same as [[CC::opt(1), CC::debug]]
  • [[using CC: CC::opt(1)]] // error: cannot combine using and scoped attribute

Storage Class Specifier

  • Auto: It was allowed in object declared in block scope or in function parameter list. Auto duration.
  • Register: It is allowed in object declared in block scope or in function parameter list. Auto duration.
  • Extern: Used for variable and object.
  • Static: It is allowed in declaration of objects, functions and anonymous unions.
  • Thread_local: It is allowed in object declared at namespace scope, function declared at block scope and and static data member.
  1. Storage duration: automatic, static, thread, dynamic.
  2. Linkage: no-linkage, internal-linkage, external-linkage.

Template Parameter

Template Parameters follow normal scoping rules.

A name used as template parameter can not be reused within template.

By default, the language assumes a name accessed through scope operator is not a type, so we use keyword typename to tell compiler that we are using a name that is a type name of template.

When we want to inform the compiler that a name represents a type, we must use the keyword typename, not class:

template <typename T>
typename T::value_type top(const T& c);     // T is a class type
  1. Template Parameters and Scope
  2. Template Declarations
  3. Using Class Members That Are Types
  4. Default template Arguments:
    template<typename T = int> void f(T a, T b);    f<string>("3", "5");    f<>(2, 3);  // use default argument
    template<class T = int> class Base;             Base<string> ba;        Base<> b;
  5. Template Default Arguments and Class Templates

Member template

  1. Category: member template for normal class and class template.
  2. When we define a member template outside of the class template we must provide class template parameter list followed function template parameter list.
  3. As usual, compiler typically deduce template arguments for the member template’s own parameter from arguments passed in the call.
template<typename T>
template<typename IT> void Base<T>::func(IT &a, IT &b) { return;  }
Base<double> base;                base.func(4, 5);
  1. Member Templates of Ordianary (Nontemplate) Classes
  2. Member Templates of Class Templates
  3. Instantiation and Member Templates

Controlling Instantiations

  1. References and pointers don't need instantiation.
  2. Compiler will instantiate a template for every file which contain this template with same arguments, In large system the overhead of this can be significant, we can avoid this through explicit instantiation.
// instantiation declaration and definition
extern template class Blob<string>;             // instantiation declaration
template int compare(const int&, const int&);   // instantiation definition
  1. An instantiation definition for a class template instantiates all the members of that template including inline member functions.
  2. When compiler sees an extern instantiation declaration it will not generate code for that instantiation until it sees that (non extern ) definition, so we must provide only one definition for that template instantiation declaration which may appear in multiple files of the program.

Efficiency And Flexibility

  1. Binding the Deleter at Run Time
  2. Binding the Deleter at Compile Time

Template Argument Deduction

Conversion and Template Type Parameter

A very limited number of conversions are supplied:

  • const conversion: A parameter that is a reference or pointer to const can be passed a reference or pointer to non-const object.
  • Array- or function-to-pointer conversion.
  • Const conversion and array or function to pointer are the only automatic conversions for arguments to parameters with template types.
  1. Function Parameters That Use Same Template Parameter Type
    • Because there are limited conversions, the arguments to parameters must have the essentially same type.
    • If the deduced types do not match, then the call is an error.
  2. Normal Conversion for Ordinary arguments

Function-Template Explicit Arguments

  1. Specifying an Explicit Template Argument
    • Explicit template argument(s) are matched to corresponding template parameter(s) from left to right.
    // poor design: users must explicitly specify all three template parameters
    template<typename T1, typename T2, typename T3> T3 alternative_sum(T2, T1);
    
    // error: can’t infer initial template parameters
    auto val3 = alternative_sum<long long>(i, lng);
    
    // ok: all three parameters are explicitly specified
    auto val2 = alternative_sum<long long, int, long>(i, lng);
  2. Normal Conversions Apply for Explicitly Specified Template Arguments
    • If we explicitly specify template parameter type, normal conversion apply.
    template<typename T> bool compare<T, T>
    compare(2.3, 4);        // error;
    compare<int>(2.3, 4);   // OK

Trailing Return Types and Type Transformation

template<typename It>
auto fcn(It beg, It end) -> decltype(*beg) {
    /* The dereference operator returns an lvalue, so
     * return a reference to an element from the range */
    return *beg;
}
  1. The Type Transformation Library Template Classes
    /* Header: <type_traits>
     * must use typename to use a type member of a template parameter; see § 16.1.3 (p. 670) */
    template <typename It>
    auto fcn2(It beg, It end) -> typename std::remove_reference<decltype(*beg)>::type {
        // return a copy of an element from the range
        return *beg;
    }

Function Pointers and Arguments Deduction

template<typename T>
int compare(const T&, const T&);

int(*pf)(const int&, const int&) = compare;

// The type of parameters int pf determines the type of template arguments for T.
void func(int (*) (const string&, const string&);
void func(int (*) (const int&, const int&);
func(compare); // error: which version of func is called?
func(compare<int>); // ok

When the address of a function-template instantiation is taken, the content must be such that it allows a unique type or value to be determined for each of template parameter.

Template Arguments Deduction and Reference

  1. Type Deduction from Lvalue Reference Function Parameter
    • When the parameter of function is a non-const ordinary reference we can only pass lvalue;
    • When it’s a const reference we can pass any kind of arguments, object, temporary or literal value.
  2. Type Deduction from Rvalue Reference Function Parameter
    • When the parameter of function is a rvalue reference we can only pass rvalue.
  3. Reference Collapsing and Rvalue Reference Parameter
  4. Reference Collapsing and Rvalue Reference Parameter
    • References collapse to form rvalue reference only in the specific case of an rvalue reference to rvalue reference.
    • Reference collapsing applies only when a reference to a reference is created indirectly, such as in a type alias or a template parameter.
    • An argument of any type can be passed to a function parameter that is a rvalue reference to a template parameter type.
  5. Writing Template Functions with Rvalue Reference Parameters

Reference collapse apper in following context:

instantiation of template; generation of auto; typedef with class type; decltype

For Mod, where Mod is If T is Then Mod::type is
remove_reference [X& or X&&] [otherwise] [X] [T]
add_const [X&, const X, or function] [otherwise] [T] [const T]
add_lvalue_reference [X&] [X&&] [otherwise] [T] [X&] [T&]
add_rvalue_reference [X& or X&&] [otherwise] [T] [T&&]
remove_pointer [X*] [otherwise] [X] [T]
add_pointer [X& or X&&] [otherwise] [X*] [T*]
make_signed [unsigned X] [otherwise] [X] [T]
make_unsigned [signed type] [otherwise] [unsigned T] [T]
remove_extent X[n] otherwise X T
remove_all_extents X[n1][n2]... otherwise X T
  • Writing Template Functions with Rvalue Reference Parameter
    • Rvalue reference are used in one of the two contexts: either the template is forwarding it parameters or the template is overloaded.

UnderStanding std::move

template <typename T>
remove_reference_t<T>&& move(T&& t) {// reference collapsing may happen
    // static_cast covered in § 4.11.3 (p. 163)
    return static_cast<remove_reference_t<T>&&>(t);
}

Forwarding:

template<typename T>
T&& forward(remove_reference_t<T>& param) {
    return static_cast<T&&>(param);
}
  1. Define Function Parameter Retain Type Information
    • A function parameter that is an rvalue reference to a template type parameter can preserve constness and lvalue / rvalue property of corresponding arguments.
  2. Using std::forward to Preserve Type Information in a Call
    • Must be used with an explicit template argument, return type is T&&.
    • When used with function parameter that is an rvalue reference to template type parameter(T&&), forward represent all details about an argument’s type.
  3. As with std::move, it’s a good idea not to provide a using declaration for std::forward.

Overloading and Templates

Function matching is affected by the presence of function template:

  • The candidate functions for a call include any function-template instantiation for which template argument deduction (§ 16.2, p. 678) succeeds.
  • The candidate function-templates are always viable, because template argument deduction will have eliminated any templates that are not viable.
  • As usual, the viable function (template and nontemplate) are ranked by the conversions, if any, needed to make a call. Of course, the conversion used to make a call are quite limited.
  • If there are several functions that provide an equally good match:
    • If there is one only nontemplate function, the nontemplate function is the best match.
    • If there is no template function, the more specialized template function is best match.
    • otherwise, the call is ambiguous.
  1. Writing Overloaded Templates
  2. Multiple Viable Templates
    • When there are several overloaded templates that provide an equally good match, the more specialized version is prefered.
  3. Nontemplate and Template Overloads
  4. Overloaded Templates and Conversions
  5. Mission Declaration Can cause Program Misbehave
    • Declare every function in an overloaded set before you define any of the function. That way you don’t worry whether the compiler will instantiate a call before it sees the function you intended to use.

Variadic Templates

The sizeof... Operator

// sizeof... operator can get parameter pack size
template<typename T, typename... Args>;
void foo(const T&, const Args&... rest);

Writing A Variadic function Template

Variadic function are always recursion.

/* function to end the recursion and print the last elements
 * must be declared before the variadic version of print is defined */
template<typename T>
ostream& printf(ostream &os, const T &t) {
    return os << t ;
}

template<typename T, typename... Args>
ostream& printf(ostream& os, const T& t, const Args&... rest) {
    os << t << ",";
    return printf(os, rest...); // recursion call
}

Pack Expansion

  1. Understanding Pack Expansions

Forwarding Parameter Packs

Template Specialization

A specialization is a separate definition of template in which one or more template parameter are specified to have particular types.

  1. Defining a Function Template Specialization

    template<typename T>
    int compare(const T&, const T&);
    
    template<>    // indicate this is a fully specialization
    int compare(const char* const &, const char*&);
  2. Function overloading Versus Template Specialization

    • Specializations instantiate a template, they do not overload it. As result, they have no effect on function matching.
    • In order to specialize a template, a declaration for the original template must be in the scope.
    • Moreover, a declaration for a specialization must be in scope before any code use that instantiation.
    • Templates and their specializations should be declared in the same header. Declaration for all template with name should appear first, followed by any specializations of those templates.
    • If declaration for a specialization missing, code works good, because compiler will generate code from original template.
  3. Class Template Specialization

    Specialize hash template:

    namespace std {
        template<> // indicates we are fully specializing a template
        struct hash<sales_data> {
            typedef size_t return_type;
            typedef sales_data arguments_type;
            size_t operator(const sales_data &) const;
        };
    
        size_t hash<sales_data>::()(const sales_data &s) const {
            return hash<string>()(s.bookNo)^
            hash<unsigned>()(s.units_sold)^
            hash<double>()(s.revenue);
        }
    }

    We can add members to namespace after open it.

  4. Class-Template Partial Specialization

    // A class partial specialization is itself a template.
    // original, most general template
    template<class T>
    struct remove_reference {typedef T type;};
    
    // partial specializations that will be used for lvalue and rvalue references
    template <class T>
    struct remove_reference<T&> // lvalue references
    { typedef T type; };
    
    template <class T>
    struct remove_reference<T&&> // rvalue references
    { typedef T type; };
  5. Specializing Members But Not Class

    template<typename T>
    struct Foo {
        Foo(const T &t = T()) : men(t){}
        void Bar();
        T men;
    };
    
    template<typename T>
    void Foo<T>::Bar() {
       std::cout << "general" << std::endl;
    }
    
    template<> // we're specializing
    void Foo<int>::Bar() {
       std::cout << "partial" << std::endl;
    }
    
    Foo<string> fs;// Foo<string>::Foo()
    fs.Bar();      // "general", Foo<string>::Bar()
    
    Foo<int> fi;    // Foo<int>::Foo()
    fi.Bar();       // "partial", uses specialization of Foo<int>::Bar()

16 Temaplte Plus (cppreference.com)

  • At link time, identical instantiations generated by different translation units are merged.
  • The definition of a template must be visible at the point of implicit instantiation, which is why template libraries typically provide all template definitions in the headers
  • Syntex
    template <parameter-list> declaration                                         (1)
    template <parameter-list> requires-clause declaration                         (2) (C++20)
    template <parameter-list> concept concept-name = constraint-expression        (4) (C++20)
  • template-id
    template-name <parameter-list>

Parameters

template <parameter-list > declaration;

Non-type template parameter

  • Syntax
    type name(optional)
    type name(optional) = default
    type ... name(optional)             (C++11)
    placeholder name                    (C++17)
  • Array and function types may be written in a template declaration, but they are automatically replaced by pointer to object and pointer to function as appropriate.
  • A template parameter of the form class Foo is not an unnamed non-type template parameter of type Foo, even if otherwise class Foo is an elaborated type specifier and class Foo x; declares x to be of type Foo.

Type template parameters

  • Syntax
    type-parameter-key name(optional)               (1)
    type-parameter-key name(optional) = default     (2)
    type-parameter-key ... name(optional)           (3) (C++11)
    type-constraint name(optional)                  (4) (C++20)
    type-constraint name(optional) = default        (5) (C++20)
    type-constraint ... name(optional)              (6) (C++20)

Template template parameter

  • Syntax
    template <parameter-list > typename(C++17)|class name(optional)            (1)
    template <parameter-list > typename(C++17)|class name(optional) = default  (2)
    template <parameter-list > typename(C++17)|class ... name(optional)        (3) (C++11)

Name resolution for template parameters

  • The name of a template parameter is not allowed to be redeclared within its scope (including nested scopes)
  • In the definition of a member of a class template that appears outside of the class template definition, the name of a member of the class template hides the name of a template parameter of any enclosing class templates, but not a template parameter of the member if the member is a class or function template.
    template<class T>
    struct A {
        struct B {};
        typedef void C;
        void f();
        template<class U> void g(U);
    };
    
    template<class B> void A<B>::f() {
        B b;            // A's B, not the template parameter
    }
    
    template<class B>
    template<class C>
    void A<B>::g(C) {
        B b;            // A's B, not the template parameter
        C c;            // the template parameter C, not A's C
    }
  • In the definition of a member of a class template that appears outside of the namespace containing the class template definition, the name of a template parameter hides the name of a member of this namespace.
    namespace N {
        class C {};
    
        template<class T>
        class B {
            void f(T);
        };
    }
    
    template<class C>
    void N::B<C>::f(C) {
        C b;            // C is the template parameter, not N::C
    }
  • In the definition of a class template or in the definition of a member of such a template that appears outside of the template definition, for each non-dependent base class, if the name of the base class or the name of a member of the base class is the same as the name of a template parameter, the base class name or member name hides the template parameter name.
    struct A {
        struct B {};
        int C;
        int Y;
    };
    
    template<class B, class C>
    struct X : A {
        B b;            // A's B
        C b;            // error: A's C isn't a type name
    };

Arguments

  • If an argument can be interpreted as both a type-id and an expression, it is always interpreted as a type-id, even if the corresponding template parameter is non-type:
    template<class T> void f(); // #1
    template<int I> void f();   // #2
    void g() {
        f<int()>(); // "int()" is both a type and an expression,
                    // calls #1 because it is interpreted as a type
    }

Non-type arguments

Type arguments

  • A template argument for a type template parameter must be a type-id, which may name an incomplete type:
    template<typename T> class X {}; // class template
    
    struct A;               // incomplete type
    typedef struct {} B;    // type alias to an unnamed type
    
    int main() {
        X<A> x1;    // ok: 'A' names a type
        X<A*> x2;   // ok: 'A*' names a type
        X<B> x3;    // ok: 'B' names a type
    }

Template template arguments

  • A template argument for a template template parameter must be an id-expression which names a class template or a template alias.

  • To match a template template argument A to a template template parameter P, P must be at least as specialized as A (C++17). If P's parameter list includes a parameter pack, zero or more template parameters (or parameter packs) from A's template parameter list are matched by it.

    template<typename T> struct eval;   // #1 primary template
    
    template<template<typename, typename...> class TT, typename T1, typename... Rest>
    struct eval<TT<T1, Rest...>> {};    // #2 partial specialization of eval
    
    template<typename T1> struct A;
    template<typename T1, typename T2> struct B;
    template<int N> struct C;
    template<typename T1, int N> struct D;
    template<typename T1, typename T2, int N = 17> struct E;
    
    eval<A<int>> eA;        // ok: matches #2
    eval<B<int, float>> eB;  // ok: matches #2
    
    eval<C<17>> eC;         // error: C does not match TT in #2
                            // because TT's first parameter is a type template parameter,
                            // while 17 does not name a type
    
    eval<D<int, 17>> eD;    // error: D does not match TT in #2
                            // because TT's second parameter is a type parameter pack,
                            // while 17 does not name a type
    
    eval<E<int, float>> eE;  // error: E does not match TT in #2
                            // because E's third (default) parameter is a non-type
    
    template<auto n> class D { /* ... */ }; // note: C++17
    template<template<int> class R> class Z { /* ... */ };
    Z<D> zd; // OK, auto -> int, P is at least as specialied as A
    
    template <int> struct SI { /* ... */ };
    template <template<auto> class> void FA();  // note: C++17
    FA<SI>();  // Error, int -> auto, P is not at least as specialied as A
  • Formally, a template template-parameter P is at least as specialized as a template template argument A if, given the following rewrite to two function templates, the function template corresponding to P is at least as specialized as the function template corresponding to A according to the partial ordering rules for function templates. Given an invented class template X with the template parameter list of A (including default arguments):

    • Each of the two function templates has the same template parameters, respectively, as P or A.
    • Each function template has a single function parameter whose type is a specialization of X with template arguments corresponding to the template parameters from the respective function template where, for each template parameter PP in the template parameter list of the function template, a corresponding template argument AA is formed. If PP declares a parameter pack, then AA is the pack expansion PP...; otherwise, AA is the id-expression PP.
    • If the rewrite produces an invalid type, then P is not at least as specialized as A.

Default template arguments

  • Defaults can be specified for any kind of template parameter (type, non-type, or template), but not to parameter packs.

  • Default parameters are not allowed:

    • in the out-of-class definition of a member of a class template (they have to be provided in the declaration inside the class body). Note that member templates of non-template classes can use default parameters in their out-of-class definitions
    • in friend template declarations, both class and function
  • On a friend function template declaration, default template arguments are allowed only if the declaration is a definition, and no other declarations of this function appear in this translation unit. (C++11)

  • Default template arguments that appear in the declarations and the definition are merged similarly to default function arguments:

    template<typename T1, typename T2 = int> class A;
    template<typename T1 = int, typename T2> class A;
    // the above is the same as the following:
    template<typename T1 = int, typename T2 = int> class A;
    • But the same parameter cannot be given default arguments twice in the same scope
  • The template parameter lists of template template parameters can have their own default arguments, which are only in effect where the template template parameter itself is in scope:

    // class template, with a type template parameter with a default
    template<typename T = float> struct B {};
    
    // template template parameter T has a parameter list, which
    // consists of one type template parameter with a default
    template<template<typename = float> typename T> struct A {
        void f();
        void g();
    };
    
    // out-of-body member function template definitions
    template<template<typename TT> class T>
    void A<T>::f() {
        T<> t; // error: TT has no default in scope
    }
    template<template<typename TT = char> class T>
    void A<T>::g() {
        T<> t; // ok: t is T<char>
    }
  • Member access for the names used in a default template parameter is checked at the declaration, not at the point of use:

    class B {};
    
    template<typename T> class C {
    protected:
        typedef T TT;
    };
    
    template<typename U, typename V = typename U::TT> class D: public U {};
    
    D<C<B>>* d; // error: C::TT is protected
  • The default template argument is implicitly instantiated when the value of that default argument is needed, except if the template is used to name a function: (C++14)

    template<typename T, typename U = int> struct S { };
    S<bool>* p; // The default argument for U is instantiated at this point
                // the type of p is S<bool, int>*

Template argument equivalence

  • Template argument equivalence is used to determine whether two template-ids are same.

Variable templates

  • Syntax
    template <parameter-list > variable-declaration

Function template

  • Syntax
    template <parameter-list> function-declaration                        (1)
    template <parameter-list> requires constraint function-declaration    (2) (C++20)
    function-declaration-with-placeholders                                (3) (C++20)

Explicit instantiation

  • Syntax

    template return-type name <argument-list> (parameter-list);         // (1)
    template return-type name (parameter-list);                         // (2) with template argument deduction
    extern template return-type name <argument-list> (parameter-list);  // (3) (C++11)
    extern template return-type name (parameter-list);                  // (4) (C++11)
  • A trailing template-argument can be left unspecified in an explicit instantiation of a function template specialization or of a member function template specialization if it can be deduced from the function parameter

    template<typename T, typename U>
    void f(T s, U s) {
        std::cout << s << " " << u << '\n';
    }
    
    template void f<double>(double, bool);  // instantiates f<double, bool>(double, bool)
    template void f<>(char, char);          // instantiates f<char, char>(char, char), template argument deduced
    template void f(int, float);             // instantiates f<int, float>(int, float), template argument deduced
  • Explicit instantiation of a function template or of a member function of a class template cannot use inline or constexpr. If the declaration of the explicit instantiation names an implicitly-declared special member function, the program is ill-formed.

  • Explicit instantiation of a constructor cannot use a template parameter list (syntax (1)), which is also never necessary because they can be deduced (syntax (2)).

  • Explicit instantiation of a prospective destructor must name the selected destructor of the class. (C++20)

  • Explicit instantiation declarations do not suppress the implicit instantiation of inline functions, auto-declarations, references, and class template specializations. (thus, when the inline function that is a subject of explicit instantiation declaration is ODR-used, it is implicitly instantiated for inlining, but its out-of-line copy is not generated in this translation unit)

  • Explicit instantiation definition of a function template with default arguments is not a use of the arguments, and does not attempt to initialize them:

    char* p = 0;
    template<class T> T g(T x = &p) { return x; }
    template int g<int>(int);   // OK even though &p isn’t an int.

Implicit instantiation

  • When code refers to a function in context that requires the function definition to exist, [or if the existence of the definition affects the semantics of the program (C++11)], and this particular function has not been explicitly instantiated, implicit instantiation occurs. The list of template arguments does not have to be supplied if it can be deduced from context.
  • Note: omitting <> entirely allows overload resolution to examine both template and non-template overloads.
template<typename T> void f(T s) {
    std::cout << s << '\n';
}

f<double>(1);   // instantiates and calls f<double>(double)
f<>('a');       // instantiates and calls f<char>(char)
f(7);           // instantiates and calls f<int>(int)
void (*ptr)(std::string) = f;   // instantiates f<string>(string)

Explicit template arguments

  • Template arguments of a function template may be obtained from
    • template argument deduction
    • default template arguments
    • specified explicitly, which can be done in the following contexts:
      • in a function call expression
      • when an address of a function is taken
      • when a reference to function is initialized
      • when a pointer to member function is formed
      • in an explicit specialization
      • in an explicit instantiation
      • in a friend declaration
  • There is no way to explicitly specify template arguments to overloaded operators, conversion functions, and constructors, because they are called without the use of the function name.
  • The specified non-type arguments must either match the types of the corresponding non-type template parameters, or be convertible to them.
  • The function parameters that do not participate in template argument deduction (e.g. if the corresponding template arguments are explicitly specified) are subject to implicit conversions to the type of the corresponding function parameter (as in the usual overload resolution).
  • A template parameter pack that is explicitly specified may be extended by template argument deduction if there are additional arguments:
    template<class ... Types> void f(Types ... values);
    void g() {
        f<int*, float*>(0, 0, 0); // Types = {int*, float*, int}
    }

Template argument deduction

  • This mechanism makes it possible to use template operators, since there is no syntax to specify template arguments for an operator other than by re-writing it as a function call expression

  • Template argument deduction takes place after the function template name lookup (which may involve argument-dependent lookup) and before template argument substitution (which may involve SFINAE) and overload resolution.

  • Template argument deduction is also performed when the name of a class template is used as the type of an object being constructed: (C++17)

    std::pair p(2, 4.5);
    std::tuple t(4, 3, 2.5);
    std::copy_n(vi1, 3, std::back_insert_iterator(vi2));
    std::for_each(vi.begin(), vi.end(), Foo([&](int i) {...}));
    auto lck = std::lock_guard(foo.mtx);
    std::lock_guard lck2(foo.mtx, ul);
    • Template argument deduction for class templates takes place in declarations and in explicit cast expressions; see class template argument deduction for details.
  • Before deduction begins, the following Parameters-Arguments-Adjustment to P and A are made:

    1. If P is not a reference type:
      • if A is an array type, A is replaced by the pointer type obtained from array-to-pointer conversion;
      • otherwise, if A is a function type, A is replaced by the pointer type obtained from function-to-pointer conversion;
      • otherwise, if A is a cv-qualified type, the top-level cv-qualifiers are ignored for deduction:
    2. If P is a cv-qualified type, the top-level cv-qualifiers are ignored for deduction.
    3. If P is a reference type, the type referred to by P is used for deduction, ignore the reference.
    4. If P is an rvalue reference to a cv-unqualified template parameter (so-called forwarding reference), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction (Reference Collapse)
      • Note: in class template argument deduction, template parameter of a class template is never a forwarding reference (C++17)
      template<class T>
      int f(T&&);       // P is an rvalue reference to cv-unqualified T (forwarding reference)
      
      template<class T>
      int g(const T&&); // P is an rvalue reference to cv-qualified T (not special)
      
      int main() {
          int i;
          int n1 = f(i);  // argument is lvalue: calls f<int&>(int&) (special case)
          int n2 = f(0);  // argument is not lvalue: calls f<int>(int&&)
      
      //  int n3 = g(i);  // error: deduces to g<int>(const int&&), which
                          // cannot bind an rvalue reference to an lvalue
      }

Deduced contexts

  • Template argument deduction attempts to determine template arguments (types for type template parameters Ti, templates for template template parameters TTi, and values for non-type template parameters Ii), which can be substituted into each parameter P to produce the type deduced A, which is the same as the type of the argument A (transformed A), after adjustments listed above.
A function call is attempted
  • If removing references and cv-qualifiers from P gives std::initializer_list<P'> and A is a braced-init-list, then deduction is performed for every element of the initializer list, taking P' as the parameter and the list element A' as the argument (C++11)

  • If removing references and cv-qualifiers from P gives P'[N], and A is a non-empty braced-init-list, then deduction is performed as above, except if N is a non-type template parameter, it is deduced from the length of the initializer list: (C++11)

    template<class T, int N> void h(T const(&)[N]);
    h({1, 2, 3}); // deduced T = int, deduced N = 3
    
    template<class T> void j(T const(&)[3]);
    j({42}); // deduced T = int, array bound is not a parameter, not considered
    
    struct Aggr { int i; int j; };
    template<int N> void k(Aggr const(&)[N]);
    k({1, 2, 3});       // error: deduction fails, no conversion from int to Aggr
    k({{1}, {2}, {3}}); // OK: deduced N = 3
    
    template<int M, int N> void m(int const(&)[M][N]);
    m({{1, 2}, {3, 4}}); // deduced M = 2, deduced N = 2
    
    template<class T, int N> void n(T const(&)[N], T);
    n({{1}, {2}, {3}}, Aggr()); // deduced T = Aggr, deduced N = 3
  • If a parameter pack appears as the last P, then the type P is matched against the type A of each remaining argument of the call. Each match deduces the template arguments for the next position in the pack expansion: (C++11)

    template<class... Types> void f(Types&...);
    void h(int x, float& y) {
        const int z = x;
        f(x, y, z); // P = Types&..., A1 = x: deduced first member of Types... = int
                    // P = Types&..., A2 = y: deduced second member of Types... = float
                    // P = Types&..., A3 = z: deduced third member of Types... = const int
                    // calls f<int, float, const int>
    }
  • If P is a function type, pointer to function type, or pointer to member function type and if A is a set of overloaded functions not containing function templates, template argument deduction is attempted with each overload. If only one succeeds, that successful deduction is used. If none or more than one succeeds, the template parameter is non-deduced context (see below):

    template<class T> int f(T(*p)(T));
    int g(int);
    int g(char);
    f(g);   // P = T(*)(T), A = overload set
            // P = T(*)(T), A1 = int(int): deduced T = int
            // P = T(*)(T), A2 = int(char): fails to deduce T
            // only one overload works, deduction succeeds
  • After these transformations, the deduction processes as described below (cf. section Deduction from type) and attempts to find such template arguments that would make the deduced A (that is, P after adjustments listed above and the substitution of the deduced template parameters) identical to the transformed A (that is, A after the adjustments listed above).

  • If the usual deduction from P and A fails, the following alternatives are additionally considered:

    1. If P is a reference type, the deduced A (i.e., the type referred to by the reference) can be more cv-qualified than the transformed A:

      template<typename T> void f(const T& t);
      bool a = false;
      f(a);   // P = const T&, adjusted to const T, A = bool:
              // deduced T = bool, deduced A = const bool
              // deduced A is more cv-qualified than A
    2. The transformed A can be another pointer or pointer to member type that can be converted to the deduced A via a qualification conversions [or a function pointer conversion (C++17)]:

      template<typename T> void f(const T*);
      int* p;
      f(p);   // P = const T*, A = int*:
              // deduced T = int, deduced A = const int*
              // qualification conversion applies (from int* to const int*)
    3. If P is a class and P has the form simple-template-id, then the transformed A can be a derived class of the deduced A. Likewise, if P is a pointer to a class of the form simple-template-id, the transformed A can be a pointer to a derived class pointed to by the deduced A:

      template<class T> struct B { };
      template<class T> struct D : public B<T> { };
      template<class T> void f(B<T>&) { }
      
      void f() {
          D<int> d;
          f(d);   // P = B<T>&, adjusted to P = B<T> (a simple-template-id), A = D<int>:
                  // deduced T = int, deduced A = B<int>
                  // A is derived from deduced A
      }
Deduction from a type
  • Given a function parameter P that depends on one or more type template parameters Ti, template template parameters TTi, or non-type template parameters Ii, and the corresponding argument A, deduction takes place if P has one of the following forms:

    T
    cv-list T
    T*
    T&
    T&&
    T[integer-constant]
    class-template-name<T>
    type(T)
    T()
    T(T)
    T type::*
    type T::*
    T T::*
    T(type::*)()
    type(T::*)()
    type(type::*)(T)
    type(T::*)(T)
    T (type::*)(T)
    T (T::*)()
    T (T::*)(T)
    type[i]
    class-template-name<I>
    TT<T>
    TT<I>
    TT<>
  • If P has one of the forms that include a template parameter list <T> or <I>, then each element Pi of that template argument list is matched against the corresponding template argument Ai of its A. If the last Pi is a pack expansion, then its pattern is compared against each remaining argument in the template argument list of A. A trailing parameter pack that is not otherwise deduced, is deduced to an empty parameter pack.

  • If P has one of the forms that include a function parameter list (T), then each parameter Pi from that list is compared with the corresponding argument Ai from A's function parameter list. If the last Pi is a pack expansion, then its declarator is compared with each remaining Ai in the parameter type list of A.

  • When the value of the argument corresponding to a non-type template parameter P that is declared with a dependent type is deduced from an expression, the template parameters in the type of P are deduced from the type of the value. (C++17)

    template <long n> struct A { };
    template <class T> struct C;
    template <class T, T n> struct C<A<n>> { using Q = T; };
    typedef long R;
    
    typedef C<A<2>>::Q R;   // OK: T was deduced to long
                            // from the template argument value in the type A<2>
    
    template <auto X> class bar{};
    template <class T, T n> void f(bar<n> x);
    f(bar<3>{});    // OK: T was deduced to int (and n to 3)
                    // from the template argument value in the type bar<3>
  • The type of N in the type T[N] is std::size_t

    template<class T, T i> void f(int (&a)[i]);
    int v[10];
    f(v);       // OK: T is std::size_t
  • If a non-type template parameter is used in the parameter list, and the corresponding template argument is deduced, the type of the deduced template argument (as specified in its enclosing template parameter list, meaning references are preserved) must match the type of the non-type template parameter exactly, except that cv-qualifiers are dropped, and except where the template argument is deduced from an array bound—in that case any integral type is allowed, even bool though it would always become true:

    template<int i> class A { };
    template<short s> void f(A<s>); // the type of the non-type template param is short
    
    void k1() {
        A<1> a;     // the type of the non-type template param of a is int
        f(a);       // P = A<(short)s>, A = A<(int)1>
                    // error: deduced non-type template argument does not have the same
                    // type as its corresponding template argument
        f<1>(a);    // OK: the template argument is not deduced,
                    // this calls f<(short)1>(A<(short)1>)
    }
    
    template<int&> struct X;
    template<int& R> void k2(X<R>&);
    int n;
    void g(X<n> &x) {
        k2(x);  // P = X<R>, A = X<n>
                // parameter type is int&
                // argument type is int& in struct X's template declaration
                // OK (with CWG 2091): deduces R to refer to n
    }
  • Type template parameter cannot be deduced from the type of a function default argument:

    template<typename T> void f(T = 5, T = 7);
    
    void g() {
        f(1);     // OK: calls f<int>(1, 7)
        f();      // error: cannot deduce T
        f<int>(); // OK: calls f<int>(5, 7)
    }
  • Deduction of template template parameter can use the type used in the template specialization used in the function call:

    template<template<typename> class X> struct A { }; // A is a template with a TT param
    template<template<typename> class TT> void f(A<TT>) { }
    template<class T> struct B { };
    A<B> ab;
    f(ab); // P = A<TT>, A = A<B>: deduced TT = B, calls f(A<B>)
auto type deduction
  • The parameter P is obtained as follows:
    • in T, the declared type of the variable that includes auto, every occurrence of auto is replaced with an imaginary type template parameter U or,
    • if the initialization is copy-list-initialization, with std::initializer_list<U>. The argument A is the initializer expression.
    • After deduction of U from P and A following the rules described above, the deduced U is substituted into P to get the actual variable type:
    const auto& x = 1 + 2;  // P = const U&, A = 1 + 2:
                            // same rules as for calling f(1 + 2) where f is
                            // template<class U> void f(const U& u)
                            // deduced U = int, the type of x is const int&
    auto l = {13};  // P = std::initializer_list<U>, A = {13}:
                    // deduced U = int, the type of l is std::initializer_list<int>
  • In direct-list-initialization (but not in copy-list-initalization), when deducing the meaning of the auto from a braced-init-list, the braced-init-list must contain only one element, and the type of auto will be the type of that element:
    auto x1 = {3};  // x1 is std::initializer_list<int>
    auto x2{1, 2};  // error: not a single element
    auto x3{3};     // x3 is int
                    // (before N3922 x2 and x3 were both std::initializer_list<int>)
auto-returning functions (C++14)
  • For auto-returning functions, the parameter P is obtained as follows:

    • in T, the declared return type of the function that includes auto, every occurrence of auto is replaced with an imaginary type template parameter U.
    • The argument A is the expression of the return statement, and if the return statement has no operand, A is void().
    • After deduction of U from P and A following the rules described above, the deduced U is substituted into T to get the actual return type:
    auto f() { return 42; } // P = auto, A = 42:
                            // deduced U = int, the return type of f is int
  • If such function has multiple return statements, the deduction is performed for each return statement. All the resulting types must be the same and become the actual return type.

  • If such function has no return statement, A is void() when deducing.

  • Note: the meaning of decltype(auto) placeholder in variable and function declarations does not use template argument deduction.

Overload resolution
  • Template argument deduction is used during overload resolution, when generating specializations from a candidate template function. P and A are the same as in a regular function call
    std::string s;
    std::getline(std::cin, s); // "std::getline" names 4 function templates,
    // 2 of which are candidate functions (correct number of parameters)
    // 1st candidate template:
    // P1 = std::basic_istream<CharT, Traits>&, A1 = std::cin
    // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s
    // deduction determines the type template parameters CharT, Traits, and Allocator
    // specialization std::getline<char, std::char_traits<char>, std::allocator<char>>
    
    // 2nd candidate template:
    // P1 = std::basic_istream<CharT, Traits>&&, A1 = std::cin
    // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s
    // deduction determines the type template parameters CharT, Traits, and Allocator
    // specialization std::getline<char, std::char_traits<char>, std::allocator<char>>
    // overload resolution ranks reference binding from lvalue std::cin and picks
    // the first of the two candidate specializations
Address of an overload set
  • Template argument deduction is used when taking an address of a overload set, which includes function templates. The function type of the function template is P. The target type is the type of A
    std::cout << std::endl; // std::endl names a function template
    // type of endl P = std::basic_ostream<CharT, Traits>& (std::basic_ostream<CharT, Traits>&)
    // operator<< parameter A = std::basic_ostream<char, std::char_traits<char>>& (*)(
    //   std::basic_ostream<char, std::char_traits<char>>&
    // )
    // (other overloads of operator<< are not viable)
    // deduction determines the type template parameters CharT and Traits
  • An additional rule is applied to the deduction in this case: when comparing function parameters Pi and Ai, if any Pi is an rvalue reference to cv-unqualified template parameter (a forwarding reference) and the corresponding Ai is an lvalue reference, then Pi is adjusted to the template parameter type (T&& becomes T).
  • If the return type of the function template is a placeholder (auto or decltype(auto)), that return type is a non-deduced context and is determined from the instantiation. (C++14)
Partial ordering
Conversion function template
  • Template argument deduction is used when selecting user-defined conversion function template arguments.

  • Paramters-Arguments-Adjustments

    • A is the type that is required as the result of the conversion. P is the return type of the conversion function template, except that
      • a) if the return type is a reference type then P is the referred type;
      • b) if the return type is an array type and A is not a reference type, then P is the pointer type obtained by array-to-pointer conversion;
      • c) if the return type is a function type and A is not a reference type, then P is the function pointer type obtained by function-to-pointer conversion;
      • d) if P is cv-qualified, the top-level cv-qualifiers are ignored.
    • If A is cv-qualified, the top-level cv-qualifiers are ignored. If A is a reference type, the referred type is used by deduction.
  • If the usual deduction from P and A (as described above) fails, the following alternatives are additionally considered:

    • a) if A is a reference type, A can be more cv-qualified than the deduced A;
    • b) if A is a pointer or pointer to member type, the deduced A is allowed to be any pointer that can be converted to A by qualification conversion:
      struct A { template<class T> operator T***(); };
      A a;
      const int* const* const* p1 = a; // P = T***, A = const int* const* const*
      // regular function-call deduction for
      // template<class T> void f(T*** p) as if called with the argument
      // of type const int* const* const* fails
      // additional deduction for conversion functions determines T = int
      // (deduced A is int***, convertible to const int* const* const*)
    • c) if A is a function pointer type, the deduced A is allowed to be pointer to noexcept function, convertible to A by function pointer conversion; (C++17)
    • d) if A is a pointer to member function, the deduced A is allowed to be a pointer to noexcept member function, convertible to A by function pointer conversion. (C++17)
Explicit instantiation
  • Template argument deduction is used in explicit instantiations, explicit (full) specializations, and those friend declarations where the declarator-id happens to refer to a specialization of a function template (for example, friend ostream& operator<< <> (...)), if not all template arguments are explicitly specified or defaulted, template argument deduction is used to determine which template's specialization is referred to.
  • P is the type of the function template that is being considered as a potential match, and A is the function type from the declaration. If there are no matches or more than one match (after partial ordering), the function declaration is ill-formed:
    template<class X> void f(X a);  // 1st template f
    template<class X> void f(X* a); // 2nd template f
    template<> void f<>(int* a) { } // explicit specialization of f
    // P1 = void(X), A1 = void(int*): deduced X = int*, f<int*>(int*)
    // P2 = void(X*), A2 = void(int*): deduced X = int, f<int>(int*)
    // f<int*>(int*) and f<int>(int*) are then submitted to partial ordering
    // which selects f<int>(int*) as the more specialized template
  • An additional rule is applied to the deduction in this case: when comparing function parameters Pi and Ai, if any Pi is an rvalue reference to cv-unqualified template parameter (a forwarding reference) and the corresponding Ai is an lvalue reference, then Pi is adjusted to the template parameter type (T&& becomes T).
Deallocation function template
  • Template argument deduction is used when determining if a deallocation function template specialization matches a given placement form of operator new.
  • P is the type of the function template that is being considered as a potential match, and A is the function type of the deallocation function that would be the match for the placement operator new under consideration. If there is no match or more than one match (after overload resolution), the placement deallocation function is not called (memory leak may occur):
    struct X {
        X() { throw std::runtime_error(""); }
        static void* operator new(std::size_t sz, bool b) { return ::operator new(sz); }
        static void* operator new(std::size_t sz, double f) { return ::operator new(sz); }
        template<typename T> static void operator delete(void* ptr, T arg) {
            ::operator delete(ptr);
        }
    };
    
    int main() {
        try {
            X* p1 = new (true) X;   // when X() throws, operator delete is looked up
                                    // P1 = void(void*, T), A1 = void(void*, bool): deduced T = bool
                                    // P2 = void(void*, T), A2 = void(void*, double): deduced T = double
                                    // overload resolution picks operator delete<bool>
        } catch(const std::exception&) { }
    
        try {
            X* p1 = new (13.2) X;   // same lookup, picks operator delete<double>
        } catch(const std::exception&) { }
    }
Implicit conversions
  • Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

  • However, if deduction succeeds for all parameters that participate in template argument deduction, and all template arguments that aren't deduced are explicitly specified or defaulted, then the remaining function parameters are compared with the corresponding function arguments. For each remaining parameter P with a type that was non-dependent before substitution of any explicitly-specified template arguments, if the corresponding argument A cannot be implicitly converted to P, deduction fails.

  • Parameters with dependent types in which no template-parameters participate in template argument deduction, and parameters that became non-dependent due to substitution of explicitly-specified template arguments will be checked during overload resolution:

    template<class T> struct Z { typedef typename T::x xx; };
    template<class T> typename Z<T>::xx f(void*, T); // #1
    template<class T> void f(int, T);                // #2
    struct A { } a;
    
    f(1, a);    // for #1, deduction determines T = struct A, but the remaining argument 1
                // cannot be implicitly converted to its parameter void*: deduction fails
                // instantiation of the return type is not requested
                // for #2, deduction determines T = struct A, and the remaining argument 1
                // can be implicitly converted to its parameter int: deduction succeeds
                // the function call compiles as a call to #2 (deduction failure is SFINAE)
Alias templates
  • Alias templates are never deduced:
    template<class T> struct Alloc { };
    template<class T> using Vec = vector<T, Alloc<T>>;
    Vec<int> v;
    
    template<template<class, class> class TT> void g(TT<int, Alloc<int>>);
    g(v); // OK: deduced TT = vector
    
    template<template<class> class TT> void f(TT<int>);
    f(v); // error: TT cannot be deduced as "Vec" because Vec is an alias template

Non-deduced contexts

    1. The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:
    // the identity template, often used to exclude specific arguments from deduction
    // (available as std::type_identity as of C++20)
    template<typename T> struct identity { typedef T type; };
    template<typename T> void bad(std::vector<T> x, T value = 1);
    template<typename T> void good(std::vector<T> x, typename identity<T>::type value = 1);
    std::vector<std::complex<double>> x;
    bad(x, 1.2);// P1 = std::vector<T>, A1 = std::vector<std::complex<double>>
                // P1/A1: deduced T = std::complex<double>
                // P2 = T, A2 = double
                // P2/A2: deduced T = double
                // error: deduction fails, T is ambiguous
    good(x, 1.2);// P1 = std::vector<T>, A1 = std::vector<std::complex<double>>
                // P1/A1: deduced T = std::complex<double>
                // P2 = identity<T>::type, A2 = double
                // P2/A2: uses T deduced by P1/A1 because T is to the left of :: in P2
                // OK: T = std::complex<double>
    1. The expression of a decltype-specifier: (C++11)
    template<typename T> void f(decltype(*std::declval<T>()) arg);
    int n;
    f<int*>(n); // P = decltype(*declval<T>()), A = int: T is in non-deduced context
    1. A non-type template argument or an array bound in which a subexpression references a template parameter:
    template<std::size_t N> void f(std::array<int, 2 * N> a);
    std::array<int, 10> a;
    f(a);   // P = std::array<int, 2 * N>, A = std::array<int, 10>:
            // 2 * N is non-deduced context, N cannot be deduced
            // note: f(std::array<int, N> a) would be able to deduce N
    1. A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done:
    template<typename T, typename F>
    void f(const std::vector<T>& v, const F& comp = std::less<T>());
    std::vector<std::string> v(3);
    f(v); // P1 = const std::vector<T>&, A1 = std::vector<std::string> lvalue
        // P1/A1 deduced T = std::string
        // P2 = const F&, A2 = std::less<std::string> rvalue
        // P2 is non-deduced context for F (template parameter) used in the
        // parameter type (const F&) of the function parameter comp,
        // that has a default argument that is being used in the call f(v)
    1. The parameter P, whose A is a function or a set of overloads such that more than one function matches P or no function matches P or the set of overloads includes one or more function templates:
    template<typename T> void out(const T& value) { std::cout << value; }
    out("123");     // P = const T&, A = const char[4] lvalue: deduced T = char[4]
    out(std::endl); // P = const T&, A = function template: T is in non-deduced context
    1. The parameter P, whose A is a braced-init-list, but P is not std::initializer_list, a reference to one (possibly cv-qualified), or a reference to an array: (c++11)
    template<class T> void g1(std::vector<T>);
    template<class T> void g2(std::vector<T>, T x);
    g1({1, 2, 3});      // P = std::vector<T>, A = {1, 2, 3}: T is in non-deduced context
                        // error: T is not explicitly specified or deduced from another P/A
    g2({1, 2, 3}, 10);  // P1 = std::vector<T>, A1 = {1, 2, 3}: T is in non-deduced context
                        // P2 = T, A2 = int: deduced T = int
    1. The parameter P which is a parameter pack and does not occur at the end of the parameter list:
    template<class... Ts, class T> void f1(T n, Ts... args);
    template<class... Ts, class T> void f2(Ts... args, T n);
    f1(1, 2, 3, 4); // P1 = T, A1 = 1: deduced T = int
                    // P2 = Ts..., A2 = 2, A3 = 3, A4 = 4: deduced Ts = [int, int, int]
    f2(1, 2, 3, 4); // P1 = Ts...: Ts is non-deduced context
    1. The template parameter list that appears within the parameter P, and which includes a pack expansion that is not at the very end of the template parameter list:
    template<int...> struct T { };
    
    template<int... Ts1, int N, int... Ts2>
    void good(const T<N, Ts1...>& arg1, const T<N, Ts2...>&);
    
    template<int... Ts1, int N, int... Ts2>
    void bad(const T<Ts1..., N>& arg1, const T<Ts2..., N>&);
    
    T<1, 2> t1;
    T<1, -1, 0> t2;
    good(t1, t2);   // P1 = const T<N, Ts1...>&, A1 = T<1, 2>:
                    // deduced N = 1, deduced Ts1 = [2]
                    // P2 = const T<N, Ts2...>&, A2 = T<1, -1, 0>:
                    // deduced N = 1, deduced Ts2 = [-1, 0]
    bad(t1, t2);    // P1 = const T<Ts1..., N>&, A1 = T<1, 2>:
                    // <Ts1..., N> is non-deduced context
                    // P2 = const T<Ts2..., N>&, A2 = T<1, -1, 0>:
                    // <Ts2..., N> is non-deduced context
    1. For P of array type (but not reference to array or pointer to array), the major array bound:
    template<int i> void f1(int a[10][i]);
    template<int i> void f2(int a[i][20]);    // P = int[i][20], array type
    template<int i> void f3(int (&a)[i][20]); // P = int(&)[i][20], reference to array
    
    int a[10][20];
    f1(a);     // OK: deduced i = 20
    f1<20>(a); // OK
    f2(a);     // error: i is non-deduced context
    f2<10>(a); // OK
    f3(a);     // OK: deduced i = 10
    f3<10>(a); // OK
  • Refer Function template argument deduction for more details.

Template argument substitution

  • When all template arguments have been specified, deduced or obtained from default template arguments, every use of a template parameter in the function parameter list is replaced with the corresponding template arguments.
  • After substitution, all function parameters of array and function type are adjusted to pointers and all top-level cv-qualifiers are dropped from function parameters (as in a regular function declaration).
  • The removal of the top-level cv-qualifiers does not affect the type of the parameter as it appears within the function:
    template <class T> void f(T t);
    template <class X> void g(const X x);
    template <class Z> void h(Z z, Z* zp);
    
    // two different functions with the same type, but
    // within the function, t has different cv qualifications
    f<int>(1);       // function type is void(int), t is int
    f<const int>(1); // function type is void(int), t is const int
    
    // two different functions with the same type and the same x
    // (pointers to these two functions are not equal,
    //  and function-local statics would have different addresses)
    g<int>(1);       // function type is void(int), x is const int
    g<const int>(1); // function type is void(int), x is const int
    
    // only top-level cv-qualifiers are dropped:
    h<const int>(1, NULL); // function type is void(int, const int*)
                           // z is const int, zp is const int*

Function template overloading

  • Function distinctions:

    • A non-template function is always distinct from a template specialization with the same type.
    • Specializations of different function templates are always distinct from each other even if they have the same type.
    • Two function templates with the same return type and the same parameter list are distinct and can be distinguished with explicit template argument list.
  • When an expression that uses type or non-type template parameters appears in the function parameter list or in the return type, that expression remains a part of the function template signature for the purpose of overloading:

    template<int I, int J> A<I+J> f(A<I>, A<J>); // overload #1
    template<int K, int L> A<K+L> f(A<K>, A<L>); // same as #1
    template<int I, int J> A<I-J> f(A<I>, A<J>); // overload #2
  • Equivalence

    • Two expressions involving template parameters are called equivalent if two function definitions that contain these expressions would be the same under ODR rules, that is, the two expressions contain the same sequence of tokens whose names are resolved to same entities via name lookup, except template parameters may be differently named.
      template <int I, int J> void f(A<I+J>);  // template overload #1
      template <int K, int L> void f(A<K+L>);  // equivalent to #1
    • Two lambda expressions are never equivalent. (C++20)
    • When determining if two dependent expressions are equivalent, only the dependent names involved are considered, not the results of name lookup. If multiple declarations of the same template differ in the result of name lookup, the first such declaration is used:
      template <class T> decltype(g(T())) h();  // decltype(g(T())) is a dependent type
      int g(int);
      template <class T> decltype(g(T())) h() { // redeclaration of h() uses earlier lookup
          return g(T());                        // ...although the lookup here does find g(int)
      }
      int i = h<int>();   // template argument substitution fails; g(int)
                          // was not in scope at the first declaration of h()
    • Two expressions involving template parameters are called functionally equivalent if they are not equivalent, but for any given set of template arguments, the evaluation of the two expressions results in the same value.
    • Two function templates are considered functionally equivalent if they are equivalent, except that one or more expressions that involve template parameters in their return types and parameter lists are functionally equivalent.
    • If a program contains declarations of function templates that are functionally equivalent but not equivalent, the program is ill-formed; no diagnostic is required.
      // equivalent
      template <int I> void f(A<I>, A<I+10>); // overload #1
      template <int I> void f(A<I>, A<I+10>); // redeclaration of overload #1
      
      // not equivalent
      template <int I> void f(A<I>, A<I+10>); // overload #1
      template <int I> void f(A<I>, A<I+11>); // overload #2
      
      // functionally-equivalent but not equivalent
      // This program is ill-formed, no diagnostic required
      template <int I> void f(A<I>, A<I+10>); // overload #1
      template <int I> void f(A<I>, A<I+1+2+3+4>); // functionally equivalent
Partial ordering
  • When the same function template specialization matches more than one overloaded function template (this often results from template argument deduction), partial ordering of overloaded function templates is performed to select the best match. The ordering is partial because there can be some templates that are considered equally specialized.

  • Partial ordering happens in:

      1. overload resolution for a call to a function template specialization
      1. when the address of a function template specialization is taken
      1. when a placement operator delete that is a function template specialization is selected to match a placement operator new
      1. when a friend function declaration, an explicit instantiation, or an explicit specialization refers to a function template specialization
      template<class X> void f(X a);  // first template f
      template<class X> void f(X* a); // second template f
      template<> void f<>(int *a) {}  // explicit specialization
      // template argument deduction comes up with two candidates:
      // f<int*>(int*) and f<int>(int*)
      // partial ordering selects f<int>(int*) as more specialized
  • To determine which of any two function templates is more specialized, partial ordering work flow:

    1. transforms one of the two templates as follows:

      • For each type, non-type, and template parameter, including parameter packs, a unique fictitious type, value, or template is generated and substituted into function type of the template
      • If only one of the two function templates being compared is a member function, and that function template is a non-static member of some class A, a new first parameter is inserted into its parameter list, whose type is cv A&& if the member function template is &&-qualified and cv A& otherwise (cv is the cv-qualification of the member function template) -- this helps the ordering of operators, which are looked up both as member and as non-member functions:
        struct A {};
        template<class T> struct B {
            template<class R> int operator*(R&);                // #1
        };
        template<class T, class R> int operator*(T&, R&);       // #2
        
        int main() {
            A a;
            B<A> b;
            b * a;  // template argument deduction for int B<A>::operator*(R&) gives R=A
                    //                             for int operator*(T&, R&), T=B<A>, R=A
            // For the purpose of partial ordering, the member template B<A>::operator*
            // is transformed into template<class R> int operator*(B<A>&, R&);
            // partial ordering between
            //     int operator*(   T&, R&)  T=B<A>, R=A
            // and int operator*(B<A>&, R&)  R=A
            // selects int operator*(B<A>&, A&) as more specialized
        }
    2. After one of the two templates was transformed as described above, template argument deduction is executed using the transformed template as the argument template and the original template type of the other template as the parameter template. The process is then repeated using the second template (after transformations) as the argument and the first template in its original form as the parameter.

    3. The types used to determine the order depend on the context:

      • in the context of a function call, the types are those function parameter types for which the function call has arguments (default function arguments, parameter packs, and ellipsis parameters are not considered -- see examples below)
      • in the context of a call to a user-defined conversion function, the return types of the conversion function templates are used
      • in other contexts, the function template type is used
    4. (Parameters-Arguments Adjustment) Each type from the list above from the parameter template is deduced. Before deduction begins, each parameter P of the parameter template and the corresponding argument A of the argument template is adjusted as follows:

      • If both P and A are reference types before, determine which is more cv-qualified (in all other cases, cv-qualificiations are ignored for partial ordering purposes)
      • If P/A is a reference type, it is replaced by the type referred to, ignore reference
      • If P/A is cv-qualified, P/A is replaced with cv-unqualified version of itself, ignore cv-qualifier
    5. After these adjustments, deduction of P from A is done following template argument deduction from a type.

  • More specialized

    • If the argument A of the transformed template-1 can be used to deduce the corresponding parameter P of template-2, but not vice versa, then this A is more specialized than P with regards to the type(s) that are deduced by this P/A pair.

    • If deduction succeeds in both directions, and the original P and A were reference types, then additional tests are made:

      • If A was lvalue reference and P was rvalue reference, A is considered to be more specialized than P
      • If A was more cv-qualified than P, A is considered to be more specialized than P
    • In all other cases, neither template is more specialized than the other with regards to the type(s) deduced by this P/A pair.

    • After considering every P and A in both directions, if, for each type that was considered,

      • template-1 is at least as specialized as template-2 for all types
      • template-1 is more specialized than template-2 for some types
      • template-2 is not more specialized than template-1 for any types OR is not at least as specialized for any types
      • Then template-1 is more specialized than template-2
  • E.g.,

    template<class T> void f(T);        // template #1
    template<class T> void f(T*);       // template #2
    template<class T> void f(const T*); // template #3
    
    const int* p;
    f(p);   // overload resolution picks: #1: void f(T ) [T = const int *]
            //                            #2: void f(T*) [T = const int]
            //                            #3: void f(const T *) [T = int]
    // partial ordering
    // #1 from transformed #2: void(T) from void(U1*): P=T  A=U1*: deduction ok: T=U1*
    // #2 from transformed #1: void(T*) from void(U1): P=T* A=U1: deduction fails
    // #2 is more specialized than #1 with regards to T
    
    // #1 from transformed #3: void(T) from void(const U1*): P=T, A=const U1*: ok
    // #3 from transformed #1: void(const T*) from void(U1): P=const T*, A=U1: fails
    // #3 is more specialized than #1 with regards to T
    
    // #2 from transformed #3: void(T*) from void(const U1*): P=T* A=const U1*: ok
    // #3 from transformed #2: void(const T*) from void(U1*): P=const T* A=U1*: fails
    // #3 is more specialized than #2 with regards to T
    // result: #3 is selected
    // in other words, f(const T*) is more specialized than f(T) or f(T*)
    template<class T> void f(T, T*);    // #1
    template<class T> void f(T, int*);  // #2
    void m(int* p) {
        f(0, p);    // deduction for #1: void f(T, T*) [T = int]
                    // deduction for #2: void f(T, int*) [T = int]
    // partial ordering:
    // #1 from #2: void(T,T*) from void(U1,int*):   P1=T,   A1=U1:   T=U1
    //                                              P2=T*   A2=int*: T=int: fails
    // #2 from #1: void(T,int*) from void(U1,U2*):  P1=T    A1=U1:   T=U1
    //                                              P2=int* A2=U2*:         fails
    // neither is more specialized w.r.t T, the call is ambiguous
    }
    template<class T> void g(T);  // template #1
    template<class T> void g(T&); // template #2
    void m() {
        float x;
        g(x);   // deduction from #1: void g(T ) [T = float]
                // deduction from #2: void g(T&) [T = float]
        // partial ordering
        // #1 from #2: void(T) from void(U1&): P=T, A=U1 (after adjustment), ok
        // #2 from #1: void(T&) from void(U1): P=T (after adjustment), A=U1: ok
        // neither is more specialized w.r.t T, the call is ambiguous
    }
    template<class T> struct A { A(); };
    
    template<class T> void h(const T&); // #1
    template<class T> void h(A<T>&);    // #2
    void m() {
        A<int> z;
        h(z);   // deduction from #1: void h(const T &) [T = A<int>]
                // deduction from #2: void h(A<T> &) [T = int]
                // partial ordering
                // #1 from #2: void(const T&) from void(A<U1>&): P=T    A=A<U1>     T=A<U1> ok
                // #2 from #1: void(A<T>&) from void(const U1&): P=A<T> A=const U1         fails
                // #2 is more specialized than #1 w.r.t T
    
        const A<int> z2;
        h(z2);  // deduction from #1: void h(const T&) [T = A<int>]
                // deduction from #2: void h(A<T>&) [T = int], but substitution fails
                // only one overload to choose from, partial ordering not tried, #1 is called
    }
    • Since in a call context considers only parameters for which there are explicit call arguments, those function parameter packs, ellipsis parameters, and parameters with default arguments, for which there is no explicit call argument, are ignored:
    template<class T>  void  f(T);               // #1
    template<class T>  void  f(T*, int=1);       // #2
    void m(int* ip) {
        int* ip;
        f(ip);     // calls #2 (T* is more specialized than T)
    }
    template<class  T>  void  g(T);             // #1
    template<class  T>  void  g(T*, ...);       // #2
    void m(int* ip) {
        g(ip);     // calls #2 (T* is more specialized than T)
    }
    template<class T, class U> struct A { };
    template<class T, class U> void f(U, A<U,T>* p = 0); // #1
    template<         class U> void f(U, A<U,U>* p = 0); // #2
    void h() {
        f<int>(42, (A<int, int>*)0);  // calls #2
        f<int>(42);                   // error: ambiguous
    }
    template<class T           >  void g(T, T = T());  // #1
    template<class T, class... U> void g(T, U ...);    // #2
    void h() {
        g(42);  // error: ambiguous
    }
    template<class  T, class... U> void f(T, U...);           // #1
    template<class  T            > void f(T);                 // #2
    void h(int i) {
        f(&i);      // calls #2 due to the tie-breaker between parameter pack and no parameter
                    // (note: was ambiguous between DR692 and DR1395)
    }
    template<class  T, class... U> void g(T*, U...);          // #1
    template<class  T            > void g(T);                 // #2
    void h(int i) {
        g(&i);        // OK: calls #1 (T* is more specialized than T)
    }
    template <class ...T> int f(T*...);  // #1
    template <class T>  int f(const T&); // #2
    f((int*)0); // OK: selects #1
                // (was ambiguous before DR1395 because deduction failed in both directions)
    template<class... Args>           void f(Args... args);               // #1
    template<class T1, class... Args> void f(T1 a1, Args... args);        // #2
    template<class T1, class T2>      void f(T1 a1, T2 a2);               // #3
    f();                // calls #1
    f(1, 2, 3);         // calls #2
    f(1, 2);            // calls #3; non-variadic template #3 is more
                        // specialized than the variadic templates #1 and #2
    • During template argument deduction within the partial ordering process, template parameters don't require to be matched with arguments, if the argument is not used in any of the types considered for partial ordering
    template <class T>          T f(int);  // #1
    template <class T, class U> T f(U);    // #2
    void g() {
        f<int>(1);  // specialization of #1 is explicit: T f(int) [T = int]
                    // specialization of #2 is deduced:  T f(U) [T = int, U = int]
        // partial ordering (only considering the argument type)
        // #1 from #2: T(int) from U1(U2): fails
        // #2 from #1: T(U) from U1(int): ok: U=int, T unused
        // calls #1
    }
    • Partial ordering of function templates containing template parameter packs is independent of the number of deduced arguments for those template parameter packs.
    template<class...> struct Tuple { };
    template<          class... Types> void g(Tuple<Types ...>);        // #1
    template<class T1, class... Types> void g(Tuple<T1, Types ...>);    // #2
    template<class T1, class... Types> void g(Tuple<T1, Types& ...>);   // #3
    
    g(Tuple<>());                       // calls #1
    g(Tuple<int, float>());             // calls #2
    g(Tuple<int, float&>());            // calls #3 [TODO:?]
    g(Tuple<int>());                    // calls #3 [TODO:?]
    • To compile a call to a function template, the compiler has to decide between non-template overloads, template overloads, and the specializations of the template overloads.
    template< class T > void f(T);              // #1: template overload
    template< class T > void f(T*);             // #2: template overload
    void                     f(double);         // #3: non-template overload
    template<>          void f(int);            // #4: specialization of #1
    
    f('a');        // calls #1
    f(new int(1)); // calls #2
    f(1.0);        // calls #3
    f(1);          // calls #4

Function overloads vs function specializations

  • Note that only non-template and primary template overloads participate in overload resolution. The specializations are not overloads and are not considered. Only after the overload resolution selects the best-matching primary function template, its specializations are examined to see if one is a better match.

Abbreviated function template (C++20)

  • When placeholder types (either auto or Concept auto) appear in the parameter list of a function declaration or of a function template declaration, the declaration declares a function template, and one invented template parameter for each placeholder is appended to the template parameter list
    void f1(auto); // same as template<class T> void f(T)
    void f2(C1 auto); // same as template<C1 T> void f2(T), if C1 is a concept
    void f3(C2 auto...); // same as template<C2... Ts> void f3(Ts...), if C2 is a concept
    void f4(const C3 auto*, C4 auto&); // same as template<C3 T, C4 U> void f4(const T*, U&);
    template <class T, C U>
    void g(T x, U y, C auto z); // same as template<class T, C U, C W> void g(T x, U y, W z);
  • Abbreviated function templates can be specialized like all function templates.
    template<>
    void f4<int>(const int*, const double&); // specialization of f4<int, const double>

Class templates

  • Syntax
    template <parameter-list> class-declaration
  • A class template by itself is not a type, or an object, or any other entity. No code is generated from a source file that contains only template definitions. In order for any code to appear, a template must be instantiated: the template arguments must be provided so that the compiler can generate an actual class (or function, from a function template).

Explicit Instatiation

  • Syntax
    template class-key template-name <argument-list>;       // Explicit instantiation definition
    extern template class-key template-name <argument-list>;// Explicit instantiation declaration (C++11)
  • Classes, functions, variables, and member template specializations can be explicitly instantiated from their templates. Member functions, member classes, and static data members of class templates can be explicitly instantiated from their member definitions.
  • An explicit instantiation declaration (an extern template) skips implicit instantiation step. This can be used to reduce compilation times by explicitly declaring a template instantiation in all but one of the source files using it, and explicitly defining it in the remaining file.
  • Explicit instantiation can only appear in the enclosing namespace of the template, unless it uses qualified-id
  • Explicit instantiation has no effect if an explicit specialization appeared before for the same set of template arguments
  • Only the declaration is required to be visible when explicitly instantiating a function template, a variable template, a member function or static data member of a class template, or a member function template.
  • The complete definition must appear before the explicit instantiation of a class template, a member class of a class template, or a member class template, unless an explicit specialization with the same template arguments appeared before.
  • When an explicit instantiation names a class template specialization, it serves as an explicit instantiation of the same kind (declaration or definition) of each of its non-inherited non-template members that has not been previously explicitly specialized in the translation unit. If this explicit instantiation is a definition, it is also an explicit instantiation definition only for the members that have been defined at this point.
  • Explicit instantiation definitions ignore member access specifiers: parameter types and return types may be private.

Implicit Instantiation

  • When code refers to a template in context that requires a completely defined type, or when the completeness of the type affects the code, and this particular type has not been explicitly instantiated, implicit instantiation occurs.
  • E.g., when an object of this type is constructed, but not when a pointer to this type is constructed.
  • This applies to the members of the class template: unless the member is used in the program, it is not instantiated, and does not require a definition.
template<class T>
struct Z {      // template definition

    void f() {}
    void g();   // never defined
};

template struct Z<double>;  // explicit instantiation of Z<double>
Z<int> a;                   // implicit instantiation of Z<int>
Z<char>* p;                 // nothing is instantiated here
p->f();                     // implicit instantiation of Z<char> and Z<char>::f() occurs here.
// Z<char>::g() is never needed and never instantiated: it does not have to be defined

Class template argument deduction (C++17)

Deduction Contexts

  • any declaration that specifies initialization of a variable and variable template

    std::pair p(2, 4.5);     // deduces to std::pair<int, double> p(2, 4.5);
    std::tuple t(4, 3, 2.5); // same as auto t = std::make_tuple(4, 3, 2.5);
    std::less l;             // same as std::less<void> l;
  • new-expressions

    template<class T> struct A { A(T,T); };
    auto y = new A{1,2}; // allocated type is A<int>
  • function-style cast expressions

    auto lck = std::lock_guard(mtx);    // deduces to std::lock_guard<std::mutex>
    std::copy_n(vi1, 3,
        std::back_insert_iterator(vi2));// deduces to std::back_insert_iterator<T>,
                                        // where T is the type of the container vi2
    std::for_each(vi.begin(), vi.end(),
        Foo([&](int i) {...}));         // deduces to Foo<T>, where T is the unique lambda type
  • the type of a non-type template parameter (C++20)

    template<class T>
    struct X {
        X(T) {}
        auto operator<=>(const X&) const = default;
    };
    
    template<X x> struct Y { };
    
    Y<0> y; // OK, Y<X<int>(0)>

Implicit deduction guides

  • When a function-style cast or declaration of a variable uses the name of a primary class template C without an argument list as the type specifier, deduction will proceed as follows:

    • If C is defined, for each constructor (or constructor template) Ci declared in the named primary template (if it is defined), a fictional function template Fi, is constructed, such that
      • template parameters of Fi are the template parameters of C followed (if Ci is a constructor template) by the template parameters of Ci (default template arguments are included too)
      • the function parameters of Fi are the constructor parameters
      • the return type of Fi is C followed by the template parameters of the class template enclosed in <>
      template<typename T>
      struct Base {
          Base(T a, T b) {}
      };
      
      template<T> Base<T> F(T a, T b); // fictional function template
    • If C is not defined or does not declare any constructors, an additional fictional function template is added, derived as above from a hypothetical constructor C()
    • In any case, an additional fictional function template derived as above from a hypothetical constructor C(C) is added, called the copy deduction candidate.
      template<T> Base<T> F(Base<T>); // copy deduction candidate
  • Template argument deduction and overload resolution is then performed for initialization of a fictional object of hypothetical class type, whose constructor signatures match the guides (except for return type) for the purpose of forming an overload set, and the initializer is provided by the context in which class template argument deduction was performed, except that the first phase of list-initialization (considering initializer-list constructors) is omitted if the initializer list consists of a single expression of type (possibly cv-qualified) U, where U is a specialization of C or a class derived from a specialization of C.

  • These fictional constructors are public members of the hypothetical class type. They are explicit if the guide was formed from an explicit constructor. If overload resolution fails, the program is ill-formed. Otherwise, the return type of the selected F template specialization becomes the deduced class template specialization.

    template<class T> struct UniquePtr { UniquePtr(T* t); };
    UniquePtr dp{new auto(2.0)};
    // One declared constructor:
    // C1: UniquePtr(T*);
    // Set of implicitly-generated deduction guides:
    // F1: template<class T> UniquePtr<T> F(T *p);
    // F2: template<class T> UniquePtr<T> F(UniquePtr<T>); // copy deduction candidate
    
    // imaginary class to initialize:
    // struct X {
    //     template<class T> X(T *p);          // from F1
    //     template<class T> X(UniquePtr<T>);  // from F2
    // };
    // direct-init of an X object with "new double(2.0)" as the initializer
    // selects the constructor that corresponds to the guide F1 with T = double
    // For F1 with T=double, the return type is UniquePtr<double>
    // result:
    // UniquePtr<double> dp{new auto(2.0)}
  • Or, for a more complex example (note: "S::N" would not compile: scope resolution qualifiers are not something that can be deduced):

    template<class T> struct S {
        template<class U> struct N {
            N(T);
            N(T, U);
            template<class V> N(V, U);
        };
    };
    
    S<int>::N x{2.0, 1};
    // the implicitly-generated deduction guides are (note that T is already known to be int)
    // F1: template<class U> S<int>::N<U> F(int);
    // F2: template<class U> S<int>::N<U> F(int, U);
    // F3: template<class U, class V> S<int>::N<U> F(V, U);
    // F4: template<class U> S<int>::N<U> F(S<int>::N<U>); (copy deduction candidate)
    // Overload resolution for direct-list-init with "{2.0, 1}" as the initializer
    // chooses F3 with U=int and V=double.
    // The return type is S<int>::N<int>
    // result:
    // S<int>::N<int> x{2.0, 1}; [TODO:? shouldn't be N<double>]
  • Aggregate deduction candidate (C++20)

    • An aggregate deduction candidate may be added, if
      • C is defined and satisfies the requirements of an aggregate type with the assumption that any dependent base class has no virtual functions or virtual base classes,
      • there are no user-defined deduction guides for C, and
      • the variable is initialized from a non-empty list of initializers arg1, arg2, ..., argn (which may use designated initializer)
    • The parameter list of the aggregate deduction candidate is produced from the aggregate element types, as follows:
      • Let ei be the (possibly recursive) aggregate element (public base/class member/array element) that would be initialized from argi, where
        • brace elision is only considered for members with non-dependent type or with array type of non-dependent bound,
        • if C (or its element that is itself an aggregate) has a base that is a pack expansion:
          • if the pack expansion is a trailing aggregate element, it is considered to match all remaining elements of the initializer list;
          • otherwise, the pack is considered to be empty.
      • If there's no such ei, the aggregate deduction candidate is not added.
      • Otherwise, determine the parameter list T1, T2, ..., Tn of the aggregate deduction candidate as follows:
        • If ei is an array and argi is either a braced-init-list or a string literal, Ti is rvalue reference to the type of ei.
        • Otherwise, Ti is the type of ei.
        • If a pack was skipped because it is a non-trailing aggregate element, an additional parameter pack of the form Pj ... is inserted in its original aggregate element position. (This will generally cause deduction to fail.)
        • If a pack is a trailing aggregate element, the trailing sequence of parameters corresponding to it is replaced by a single parameter of the form Tn ....
    • The aggregate deduction candidate is a fictional function template derived as above from a hypothetical constructor C(T1, T2, ..., Tn).
    • During template argument deduction for the aggregate deduction candidate, the number of elements in a trailing parameter pack is only deduced from the number of remaining function arguments if it is not otherwise deduced.
    template<class T> struct A { T t; struct { long a, b; } u; };
    A a{1, 2, 3}; // aggregate deduction candidate: template<class T> A<T> F(T, long, long);
    
    template<class... Args>
    struct B : std::tuple<Args...>, Args... {};
    B b{ std::tuple<std::any, std::string>{}, std::any{} };
    // aggregate deduction candidate:
    //   template<class... Args> B<Args...> F(std::tuple<Args...>, Args...);
    // type of b is deduced as B<std::any, std::string>

User-defined deduction guides

  • The syntax of a user-defined deduction guide is the syntax of a function declaration with a trailing return type, except that it uses the name of a class template as the function name:
    explicit-specifier(optional) template-name (parameter-declaration-clause) -> simple-template-id;
  • User-defined deduction guides must name a class template and must be introduced within the same semantic scope of the class template (which could be namespace or enclosing class) and, for a member class template, must have the same access, but deduction guides do not become members of that scope.
  • A deduction guide is not a function and does not have a body. Deduction guides are not found by name lookup and do not participate in overload resolution except for the overload resolution against other deduction guides when deducing class template arguments. Deduction guides cannot be redeclared in the same translation unit for the same class template.
    // declaration of the template
    template<class T> struct container {
        container(T t) {}
        template<class Iter> container(Iter beg, Iter end);
    };
    // additional deduction guide
    template<class Iter>
    container(Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
    
    // uses
    container c(7); // OK: deduces T=int using an implicitly-generated guide
    std::vector<double> v = { /* ... */};
    auto d = container(v.begin(), v.end()); // OK: deduces T=double
    container e{5, 6}; // Error: there is no std::iterator_traits<int>::value_type
  • The fictional constructors for the purpose of overload resolution (described above) are explicit if they correspond to an implicitly-generated deduction guide formed from an explicit constructor or to a user-defined deduction guide that is declared explicit. As always, such constructors are ignored in copy-initialization context:
    template<class T> struct A {
        explicit A(const T&, ...) noexcept; // #1
        A(T&&, ...);                        // #2
    };
    
    int i;
    A a1 = { i, i }; // error: cannot deduce from rvalue reference in #2,
                     // and #1 is explicit, and not considered in copy-initialization.
    A a2{i, i};      // OK, #1 deduces to A<int> and also initializes
    A a3{0, i};      // OK, #2 deduces to A<int> and also initializes
    A a4 = {0, i};   // OK, #2 deduces to A<int> and also initializes
    
    template<class T> A(const T&, const T&) -> A<T&>; // #3
    template<class T> explicit A(T&&, T&&)  -> A<T>;  // #4
    
    A a5 = {0, 1};   // error: #3 deduces to A<int&>
                     // and #1 & #2 result in same parameter constructors.
    A a6{0,1};       // OK, #4 deduces to A<int> and #2 initializes
    A a7 = {0, i};   // error: #3 deduces to A<int&>
    A a8{0,i};       // error: #3 deduces to A<int&>
  • Using a member typedef or alias template in a constructor or constructor template's parameter list does not, by itself, render the corresponding parameter of the implicitly generated guide a non-deduced context.
    template<class T> struct B {
        template<class U> using TA = T;
        template<class U> B(U, TA<U>);  //#1
    };
    
    // Implicit deduction guide generated from #1 is the equivalent of
    // template<class T, class U> B(U, T) -> B<T>;
    // rather than
    // template<class T, class U> B(U, typename B<T>::template TA<U>) -> B<T>;
    // which would not have been deducible
    
    B b{(int*)0, (char*)0}; // OK, deduces B<char*>

Deduction for alias templates (C++20)

TODO

Notes

  • Class template argument deduction is only performed if no template argument list is present. If a template argument list is specified, deduction does not take place.

    std::tuple t1(1, 2, 3);              // OK: deduction
    std::tuple<int,int,int> t2(1, 2, 3); // OK: all arguments are provided
    std::tuple<> t3(1, 2, 3);            // Error: no matching constructor in tuple<>. No deduction performed.
    std::tuple<int> t4(1, 2, 3);         // Error
  • Class template argument deduction of aggregates typically requires user-defined deduction guides: (C++20)

    template<class A, class B> struct Agg { A a; B b; };
    // implicitly-generated guides are formed from default, copy, and move constructors
    template<class A, class B> Agg(A a, B b) -> Agg<A, B>;
    // ^ This deduction guide can be implicitly generated in C++20
    Agg agg{1, 2.0}; // deduced to Agg<int, double> from the user-defined guide
    
    template <class... T>
    array(T&&... t) -> array<std::common_type_t<T...>, sizeof...(T)>;
    auto a = array{1, 2, 5u}; // deduced to array<unsigned, 3> from the user-defined guide
  • User-defined deduction guides do not have to be templates:

    template<class T> struct S { S(T); };
    S(char const*) -> S<std::string>;
    S s{"hello"}; // deduced to S<std::string>
  • Within the scope of a class template, the name of the template without a parameter list is an injected class name, and can be used as a type. In that case, class argument deduction does not happen and template parameters must be supplied explicitly:

    template<class T>
    struct X {
        X(T) { }
        template<class Iter> X(Iter b, Iter e) { }
    
        template<class Iter>
        auto foo(Iter b, Iter e) {
            return X(b, e); // no deduction: X is the current X<T>
        }
    
        template<class Iter>
        auto bar(Iter b, Iter e) {
            return X<typename Iter::value_type>(b, e); // must specify what we want
        }
    
        auto baz() {
            return ::X(0); // not the injected-class-name; deduced to be X<int>
        }
    };
  • In overload resolution, partial ordering takes precedence over whether a function template is generated from a user-defined deduction guide: if the function template generated from the constructor is more specialized than the one generated from the user-defined deduction guide, the one generated from the constructor is chosen. Because the copy deduction candidate is typically more specialized than a wrapping constructor, this rule means that copying is generally preferred over wrapping.

    template<class T> struct A {
        A(T, int*);     // #1
        A(A<T>&, int*); // #2
        enum { value };
    };
    template<class T, int N = T::value> A(T&&, int*) -> A<T>; //#3
    
    A a{1,0}; // uses #1 to deduce A<int> and initializes with #1
    A b{a,0}; // uses #2 (more specialized than #3) to deduce A<int> and initializes with #2
  • [Overload Resolution] When earlier tiebreakers, including partial ordering, failed to distinguish between two candidate function templates, the following rules apply:

    • User-defined > implicit: A function template generated from a user-defined deduction guide is preferred over one implicitly generated from a constructor or constructor template.
    • Copy deduction > implicit: The copy deduction candidate is preferred over all other function templates implicitly generated from a constructor or constructor template.
    • non-template > implicit: A function template implicitly generated from a non-template constructor is preferred over a function template implicitly generated from a constructor template.
    template <class T> struct A {
        using value_type = T;
        A(value_type);          // #1
        A(const A&);            // #2
        A(T, T, int);           // #3
        template<class U>
        A(int, T, U);           // #4
    };                          // A(A); #5, the copy deduction candidate
    
    A x (1, 2, 3);              // uses #3, generated from a non-template constructor
    
    template <class T> A(T) -> A<T>;  // #6, less specialized than #5
    
    A a (42); // uses #6 to deduce A<int> and #1 to initialize
    A b = a;  // uses #5 to deduce A<int> and #2 to initialize
    
    template <class T> A(A<T>) -> A<A<T>>;  // #7, as specialized as #5
    
    A b2 = a;  // uses #7 to deduce A<A<int>> and #1 to initialize
  • An rvalue reference to a cv-unqualified template parameter is not a forwarding reference if that parameter is a class template parameter:

    template<class T> struct A {
        template<class U>
        A(T&&, U&&, int*);      // #1: T&& is not a forwarding reference, T&& can't collapse
                                //     U&& is a forwarding reference
        A(T&&, int*);           // #2: T&& is not a forwarding reference
    };
    
    template<class T> A(T&&, int*) -> A<T>; // #3: T&& is a forwarding reference, T&& can collapse
    
    int i, *ip;
    A a{i, 0, ip};  // error, cannot deduce from #1
    A a0{0, 0, ip}; // uses #1 to deduce A<int> and #1 to initialize
    A a2{i, ip};    // uses #3 to deduce A<int&> and #2 to initialize
  • When initializing from a single argument of a type that is a specialization of the class template at issue, copying deduction is generally preferred over wrapping by default:

    std::tuple t1{1};   //std::tuple<int>
    std::tuple t2{t1};  //std::tuple<int>, not std::tuple<std::tuple<int>>
    
    std::vector v1{1, 2};   // std::vector<int>
    std::vector v2{v1};     // std::vector<int>, not std::vector<std::vector<int>> (P0702R1)
    std::vector v3{v1, v2}; // std::vector<std::vector<int>>
  • Outside the special case for copying vs. wrapping, the strong preference for initializer-list constructors in list-initialization remains intact.

    std::vector v1{1, 2}; // std::vector<int>
    
    std::vector v2(v1.begin(), v1.end());  // std::vector<int>
    std::vector v3{v1.begin(), v1.end()};  // std::vector<std::vector<int>::iterator>
  • Before class template argument deduction was introduced, a common approach to avoiding explicitly specifying arguments is to use a function template:

    std::tuple p1{1, 1.0};             //std::tuple<int, double>, using deduction
    auto p2 = std::make_tuple(1, 1.0); //std::tuple<int, double>, pre-C++17

Class Member template

  • Template declarations (class, function, and variables (C++14)) can appear inside a member specification of any class, struct, or union that aren't local classes.
  • Partial specializations of member template may appear both at class scope and at enclosing namespace scope, but explicit (full) specializations may only appear in any scope in which the primary template may appear.
  • If the enclosing class declaration is, in turn, a class template, when a member template is defined outside of the class body, it takes two sets of template parameters: one for the enclosing class, and another one for itself.

Member function templates

  • Destructors and copy constructors cannot be templates. If a template constructor is declared which could be instantiated with the type signature of a copy constructor, the implicitly-declared copy constructor is used instead.

    • 635. Names of constructors and destructors of templates

      template <class> struct S { S(); ~S(); };
      template <class T> S<T>::S<T>() { }        // error
      template <class T> S<T>::~S<T>() { }       // okay

      The reason for this is that 3.4.3.1 [class.qual] paragraph 2 says that S::S is "considered to name the constructor," which is not a template and thus cannot accept a template argument list. On the other hand, the second S in S::~S finds the injected-class-name, which "can be used with or without a template-argument-list" (14.6.1 [temp.local] paragraph 1) and thus satisfies the requirement to name the destructor's class (12.4 [class.dtor] paragraph 1).

  • A member function template cannot be virtual, and a member function template in a derived class cannot override a virtual member function from the base class.

  • A non-template member function and a template member function with the same name may be declared. In case of conflict (when some template specialization matches the non-template function signature exactly), the use of that name and type refers to the non-template member unless an explicit template argument list is supplied.

    template<typename T>
    struct A {
        void f(int); // non-template member
    
        template<typename T2>
        void f(T2); // member template
    };
    
    int main() {
        A<char> ac;
        ac.f('c'); // calls template function A<char>::f<char>(int)
        ac.f(1);   // calls non-template function A<char>::f(int)
        ac.f<>(1); // calls template function A<char>::f<int>(int)
    }
  • An out-of-class definition of a member function template must be equivalent to the declaration inside the class (see function template overloading for the definition of equivalency), otherwise it is considered to be an overload.

    struct X {
        template<class T> T good(T n);
        template<class T> T bad(T n);
    };
    
    template<class T> struct identity { using type = T; };
    
    // OK: equivalent declaration
    template<class V>
    V X::good(V n) { return n; }
    
    // Error: not equivalent to any of the declarations inside X
    template<class T>
    T X::bad(typename identity<T>::type n) { return n; }

Member class templates

Conversion function templates

  • A user-defined conversion function can be a template.
  • A user-defined conversion function template cannot have a deduced return type. (C++14)
  • During overload resolution, specializations of conversion function templates are not found by name lookup. Instead, all visible conversion function templates are considered, and every specialization produced by template argument deduction (which has special rules for conversion function templates) is used as if found by name lookup.
  • Using-declarations in derived classes cannot refer to specializations of template conversion functions from base classes.
struct A {
    template<typename T>
    operator T*(); // conversion to pointer to any type
};

// out-of-class definition
template<typename T>
A::operator T*() {return nullptr;}

// explicit specialization for char*
template<>
A::operator char*() {return nullptr;}

// explicit instantiation
template A::operator void*();

int main() {
    A a;
    int* ip = a.operator int*(); // explicit call to A::operator int*()
}

Member variable templates

  • A variable template declaration may appear at class scope, in which case it declares a static data member template. (C++14)

Explicit (full) specialization

  • Syntax
    template <> declaration
  • Any of the following can be fully specialized:
    1. function template
    2. class template
    3. (C++14)variable template
    4. member function of a class template
    5. member function template of a class or class template
    6. static data member of a class template
    7. member class of a class template
    8. member class template of a class or class template
    9. member enumeration of a class template

In detail

  • Explicit specialization may be declared in any scope where its primary template may be defined (which may be different from the scope where the primary template is defined; such as with out-of-class specialization of a member template) . Explicit specialization has to appear after the non-specialized template declaration.

  • Specialization must be declared before the first use that would cause implicit instantiation, in every translation unit where such use occurs

    class String {};
    template<class T> class Array { };
    template<class T> void sort(Array<T>& v) { } // primary template
    
    void f(Array<String>& v) {
        sort(v); // implicitly instantiates sort(Array<String>&),
    }            // using the primary template for sort()
    
    template<>  // ERROR: explicit specialization of sort(Array<String>)
    void sort<String>(Array<String>& v); // after implicit instantiation
  • A template specialization that was declared but not defined can be used just like any other incomplete type (e.g. pointers and references to it may be used)

    template<class T> class X;  // primary template
    template<> class X<int>;    // specialization (declared, not defined)
    X<int>* p;  // OK: pointer to incomplete type
    X<int> x;   // error: object of incomplete type

Explicit specializations of function templates

  • When specializing a function template, its template arguments can be omitted if template argument deduction can provide them from the function arguments
    template<class T> class Array { };
    template<class T> void sort(Array<T>& v);   // primary template
    template<> void sort(Array<int>&);          // specialization for T = int
    // no need to write
    // template<> void sort<int>(Array<int>&);
  • A function with the same name and the same argument list as a specialization is not a specialization (see template overloading in function template)
  • An explicit specialization of a function template is inline only if it is declared with the inline specifier (or defined as deleted), it doesn't matter if the primary template is inline.
  • Default function arguments cannot be specified in explicit specializations of function templates, member function templates, and member functions of class templates when the class is implicitly instantiated.
  • An explicit specialization cannot be a friend declaration. TODO:?
  • If the primary template has a exception specification that isn't noexcept(false), the explicit specializations must have a compatible exception specification.

Members of specializations

  • When defining a member of an explicitly specialized class template outside the body of the class, the syntax template <> is not used, except if it's a member of an explicitly specialized member class template, which is specialized as a class template, because otherwise, the syntax would require such definition to begin with template required by the nested template

    template< typename T>
    struct A {
        struct B {};  // member class
        template<class U> struct C { }; // member class template
    };
    
    // 1. specialization of a member function
    template<>
    struct A<int> {
        void af(int);           // member function of a specialization
    };
    
    void A<int>::af(int) { }    // template<> not used for a member of a specialization
    
    // 2. specialization of a member class
    template<>
    struct A<char>::B {
        void bf();
    };
    
    void A<char>::B::bf() { }   // template<> not used for a member of a specialized member class either
    
    // 3. specialization of a member class template
    template<>
    template<class U>
    struct A<char>::C {
        void cf();
    };
    
    // template<> is used when defining a member of an explicitly
    // specialized member class template specialized as a class template
    template<>
    template<class U>
    void A<char>::C<U>::cf() { }
    
    template<>
    template<>
    void A<char>::C<char>::cf() { }
  • An explicit specialization of a static data member of a template is a definition if the declaration includes an initializer; otherwise, it is a declaration. These definitions must use braces for default initialization:

    template<> X Q<int>::x;    // declaration of a static member
    template<> X Q<int>::x (); // error: function declaration
    template<> X Q<int>::x {}; // definition of a default-initialized static member
  • A member or a member template of a class template may be explicitly specialized for a given implicit instantiation of the class template, even if the member or member template is defined in the class template definition.

    template<typename T>
    struct A {
        void f(T); // member, declared in the primary template
        void h(T) {} // member, defined in the primary template
        template<class X1> void g1(T, X1); // member template
        template<class X2> void g2(T, X2); // member template
    };
    
    // specialization of a member
    template<> void A<int>::f(int);
    // member specialization OK even if defined in-class
    template<> void A<int>::h(int) {}
    
    // out of class member template definition
    template<class T>
    template<class X1>
    void A<T>::g1(T, X1) { }
    
    // member template specialization
    template<>
    template<class X1>
    void A<int>::g1(int, X1);
    
    // member template specialization
    template<>
    template<>
    void A<int>::g2<char>(int, char); // for X2 = char
    
    // same, using template argument deduction (X1 = char)
    template<>
    template<>
    void A<int>::g1(int, char);
  • Member or a member template may be nested within many enclosing class templates. In an explicit specialization for such a member, there's a template<> for every enclosing class template that is explicitly specialized.

    template<class T1>
    struct A {
        template<class T2>
        struct B {
            template<class T3>
            void mf();
        };
    };
    template<> struct A<int>;
    template<> template<> struct A<char>::B<double>;
    template<> template<> template<> void A<char>::B<char>::mf<double>();
  • In such a nested declaration, some of the levels may remain unspecialized (except that it can't specialize a class member template if its enclosing class is unspecialized). For each of those levels, the declaration needs template, because such specializations are themselves templates.

    template <class T1>
    class A {
        template<class T2>
        class B {
            template<class T3>
            void mf1(T3);   // member template
    
            void mf2();     // non-template member
        };
    };
    
    // specialization
    template<>          // for the specialized A
    template<class X>   // for the unspecialized B
    class A<int>::B {
        template <class T>
        void mf1(T);
    };
    
    // specialization
    template<> // for the specialized A
    template<> // for the specialized B
    template<class T> // for the unspecialized mf1
    void A<int>::B<double>::mf1(T t) { }
    
    // ERROR: B<double> is specialized and is a member template, so its enclosing A
    // must be specialized also
    template<class Y>
    template<>
    void A<Y>::B<double>::mf2() { }

Partial specialization

  • Syntax

    template <parameter-list> class-key class-head-name <argument-list> declaration                 (1)
    template <parameter-list> decl-specifier-seq declarator <argument-list> initializer(optional)   (2) (C++14)
  • Allows customizing class and variable (C++14) templates for a given category of template arguments.

  • This declaration must be in the same namespace or, for member templates, class scope as the primary template definition which it specializes.

template<class T1, class T2, int I>
class A {};             // primary template

template<class T, int I>
class A<T, T*, I> {};   // #1: partial specialization where T2 is a pointer to T1

template<class T, class T2, int I>
class A<T*, T2, I> {};  // #2: partial specialization where T1 is a pointer

template<class T>
class A<int, T*, 5> {}; // #3: partial specialization where T1 is int, I is 5, and T2 is a pointer

template<class X, class T, int I>
class A<X, T*, I> {};   // #4: partial specialization where T2 is a pointer

The argument list

  • The following restrictions apply to the argument-list of a partial template specialization:
    • The specialization has to be more specialized than the primary template (C++11)

      template<int N, typename T1, typename... Ts> struct B;
      template<typename... Ts> struct B<0, Ts...> { }; // Error: not more specialized
    • Default arguments cannot appear in the argument list

    • If any argument is a pack expansion, it must be the last argument in the list

    • Non-type argument expressions can use template parameters as long as the parameter appears at least once outside a non-deduced context (note that only clang supports this feature currently)

    • Non-type template argument cannot specialize a template parameter whose type depends on a parameter of the specialization

      template <class T, T t> struct C {}; // primary template
      template <class T> struct C<T, 1>;   // error: type of the argument 1 is T, which depends on the parameter T
      
      template<int X, int (*array_ptr)[X]> class B {}; // primary template
      int array[5];
      template<int X> class B<X, &array> { };    // error: type of the argument &array is int(*)[X], which depends on the parameter X

Name lookup

  • Partial template specializations are not found by name lookup. Only if the primary template is found by name lookup, its partial specializations are considered. In particular, a using declaration that makes a primary template visible, makes partial specializations visible as well.

Partial ordering

  • When a class or variable (C++14) template is instantiated, and there are partial specializations available, the compiler has to decide if the primary template is going to be used or one of its partial specializations.

    • If only one specialization matches the template arguments, that specialization is used
    • If more than one specialization matches, partial order rules are used to determine which specialization is more specialized. The most specialized specialization is used, if it is unique (if it is not unique, the program cannot be compiled)
    • If no specializations match, the primary template is used
  • Overload Resolution To establish more-specialized-than relationship between partial specializations, each is first converted to a fictitious function template as follows:

    • The first function template has the same template parameters as the first partial specialization and has just one function parameter, whose type is a class template specialization with all the template arguments from the first partial specialization
    • the second function template has the same template parameters as the second partial specialization and has just one function parameter whose type is a class template specialization with all the template arguments from the second partial specialization.
    • The function templates are then ranked as if for function template overloading.
    template<int I, int J, class T> struct X { }; // primary template
    
    template<int I, int J>
    struct X<I, J, int> {       // partial specialization #1
        static const int s = 1; // fictitious function template is template<int I, int J> void f(X<I, J, int>); #A
    };
    
    template<int I>
    struct X<I, I, int> {       // partial specialization #2
        static const int s = 2; // fictitious function template is template<int I> void f(X<I, I, int>); #B
    };
    
    int main() {
        X<2, 2, int> x; // both #1 and #2 match
        // partial ordering for function templates:
        // #A from #B: void(X<I,J,int>) from void(X<U1, U1, int>): deduction ok
        // #B from #A: void(X<I,I,int>) from void(X<U1, U2, int>): deduction fails
        // #B is more specialized
        // #2 is the specialization that is instantiated
        std::cout << x.s << '\n'; // prints 2
    }

Members of partial specializations

  1. The template parameter list and the template argument list of a member of a partial specialization must match the parameter list and the argument list of the partial specialization.
  2. Just like with members of primary templates, they only need to be defined if used in the program.
  3. Members of partial specializations are not related to the members of the primary template.
  4. Explicit (full) specialization of a member of a partial specialization is declared the same way as an explicit specialization of the primary template.
    template<class T, int I>  // primary template
    struct A {
        void f(); // member declaration
    };
    
    template<class T, int I>
    void A<T,I>::f() { } // primary template member definition
    
    // partial specialization
    template<class T>
    struct A<T,2> {
        void f();
        void g(); // Rule 3
        void h(); // Rule 3
    };
    
    // member of partial specialization
    template<class T>
    void A<T,2>::g() { }
    
    // explicit (full) specialization of a member of partial specialization
    template<>
    void A<char,2>::h() {}
    
    int main() {
        A<char,0> a0;
        A<char,2> a2;
        a0.f(); // OK, uses primary template’s member definition
        a2.g(); // OK, uses partial specialization's member definition
        a2.h(); // OK, uses fully-specialized definition of the member of a partial specialization
        a2.f(); // error: no definition of f() in the partial
                // specialization A<T,2> (the primary template is not used)
    }
  5. If a primary template is a member of another class template, its partial specializations are members of the enclosing class template.
    • If the enclosing template is instantiated, the declaration of each member partial specialization is instantiated as well (the same way declarations, but not definitions, of all other members of a template are instantiated).
  6. If the primary member template is explicitly (fully) specialized for a given (implicit) specialization of the enclosing class template, the partial specializations of the member template are ignored for this specialization of the enclosing class template.
  7. If a partial specialization of the member template is explicitly specialized for a given (implicit) specialization of the enclosing class template, the primary member template and its other partial specializations are still considered for this specialization of the enclosing class template.
    template<class T>
    struct A {                  // enclosing class template
        template<class T2>
        struct B {};            // primary member template
    
        template<class T2>
        struct B<T2*> {};       // partial specialization of member template
    };
    
    template<>
    template<class T2>
    struct A<short>::B {};      // full specialization of primary member template
                                // (will ignore the partial)
    
    A<char>::B<int> abci;       // uses primary
    A<char>::B<int*> abcip;     // uses partial specialization T2=int
    A<short>::B<int*> absip;    // uses full specialization of the primary (ignores partial)

Dependent names

Parameter packs (C++11)

  • Syntax
    type ... Args(optional)                     // (1) non-type template parameter pack
    typename|class ... Args(optional)           // (2) type template parameter pack
    template <parameter-list> typename(C++17)|class ... Args(optional) // (3) template template parameter pack
    Args ... args(optional)                     // (4) function parameter pack
    pattern ...	                                // (5) Parameter pack expansion
  • In a primary class template, the template parameter pack must be the final parameter in the template parameter list. In a function template, the template parameter pack may appear earlier in the list provided that all following parameters can be deduced from the function arguments, or have default arguments:
    template<typename... Ts, typename U> struct Invalid; // Error: Ts.. not at the end
    
    template<typename ...Ts, typename U, typename=void>
    void valid(U, Ts...);     // OK: can deduce U
    // void valid(Ts..., U);  // Can't be used: Ts... is a non-deduced context in this position
    
    valid(1.0, 1, 2, 3);      // OK: deduces U as double, Ts as {int,int,int}

Pack expansion

  • A pattern followed by an ellipsis, in which the name of at least one parameter pack appears at least once, is expanded into zero or more comma-separated instantiations of the pattern, where the name of the parameter pack is replaced by each of the elements from the pack, in order.
  • If the names of two parameter packs appear in the same pattern, they are expanded simultaneously, and they must have the same length:
  • If a pack expansion is nested within another pack expansion, the parameter packs that appear inside the innermost pack expansion are expanded by it, and there must be another pack mentioned in the enclosing pack expansion, but not in the innermost one:
    template<class ...Args>
        void g(Args... args) {
            f(const_cast<const Args*>(&args)...);
    // const_cast<const Args*>(&args) is the pattern, it expands two packs
    // (Args and args) simultaneously
    
            f(h(args...) + args...); // Nested pack expansion:
    // inner pack expansion is "args...", it is expanded first
    // outer pack expansion is h(E1, E2, E3) + args..., it is expanded
    // second (as h(E1,E2,E3) + E1, h(E1,E2,E3) + E2, h(E1,E2,E3) + E3)
    }

Expansion loci

  • Function argument lists
  • Function parameter list
  • Template parameter list
  • Template argument lists
  • Parenthesized initializers
  • Brace-enclosed initializers
  • Lambda captures
  • The sizeof... operator
  • Dynamic exception specifications
  • Base specifiers and member initializer lists

Fold-expressions (C++17)

  • Syntax
    ( pack op ... )         // (1) unary right fold
    ( ... op pack )         // (2) unary left fold
    ( pack op ... op init ) // (3) binary right fold
    ( init op ... op pack ) // (4) binary left fold

SFINAE

  • "Substitution Failure Is Not An Error"

Constraints and concepts (C++20)

17 Specialized Library Facilities

A tuple can be thought of as a "quick and dirty" data struct.

Tuple type:

tuple<T1, T2, T3...> t;  tuple<T1, T2, T3...>  t(V1, V2, V3..);  make_tuple(V1, V2, V3...);  get<i>(t);
tuple_size<tupleType>::value;    tuple_element<i, tupleType>::type;    swap(...);

Bitset:

bitset<n> b; bitset<n> b(u);  biset<n> b(s, pos, m, zero, one);  bitset<n> b(cp, pos, m, zero, one);
any()  all()  none()  count()  test(pos)  set(pos, v)  set()  reset(pos)  flip(pos)  flip()

Regular Expression:

Regex  regex_match  regex_search  regex_replace  regex_iterator  smatch ssub_match
(seq, m, r, mfg)  (seq, r, mft)

18 Tools for Large Programs

18.2 Namespace

18.2.1 Namespace Definition

  1. Each namespace is scope
  2. Namespace can be discontiguous
  3. Namespaces that defien multiple, unrelated types, should use separate files to represent each types that the namespace defines.
  4. Namespace members are possible to defined outside the namespace, but not in the unrelated namespace.
  5. inline namespace: can be used as if they were direct members of the enclosing namespace.
  6. unnamed namespace, variables defiend in an unnamed space have static lifetime: they are created before their first use and destroyed when the program ends.
  7. The use of file static declarations is deprecated by the C++ standard. File static should be avoided and unnamespaces used instead.

18.2.2 Using namespace members

Namespace aliases

namespace cplusplus_primer{};
namespace primer  = cplusplus_primer;
  1. using Declaration
    • introduce namespace members into other namespaces and block scopes
    • introduce base class members into derived class definitions
      • Introduces a member of a base class into the derived class definition, such as to expose a protected member of base as public member of derived.
      • If the name is the name of an overloaded member function of the base class, all base class member functions with that name are introduced
      • If the derived class already has a member with the same name, parameter list, and qualifications, the derived class member hides or overrides (doesn't conflict with) the member that is introduced from the base class.
    • Inheriting constructors
      • If the using-declaration refers to a constructor of a direct base of the class being defined (e.g. using Base::Base;), all constructors of that base (ignoring member access) are made visible to overload resolution when initializing the derived class.
      • If overload resolution selects one of the inherited constructors when initializing an object of such derived class:
        • Then the Base subobject from which the constructor was inherited is initialized using the inherited constructor
        • And all other bases and members of Derived are initialized as if by the defaulted default constructor (default member initializers are used if provided, otherwise default initialization takes place).
        • The entire initialization is treated as a single function call: initialization of the parameters of the inherited constructor is sequenced-before initialization of any base or member of the derived object.
      • If the constructor was inherited from multiple base class subobjects of type B, the program is ill-formed, similar to multiply-inherited non-static member functions
      • If an inherited constructor matches the signature of one of the constructors of Derived, it is hidden from lookup by the version found in Derived
      • If one of the inherited constructors of Base happens to have the signature that matches a copy/move constructor of the Derived, it does not prevent implicit generation of Derived copy/move constructor (which then hides the inherited version, similar to using operator=).
    • introduce enumerators into namespaces, block, and class scopes (C++20)
      • A using-declaration can also be used with unscoped enumerators.
  2. using Directives
    • Allows us to use the unqualied form of a namespace name. Unlike a using declaration, we retain no control over which names are made visile---- they all are.
    using namespace std::string;
  3. Using directives and scope

18.2.3 Classes, Namespaces, and Scope

Arguemnt-Dependent Lookup and Parameter of Class Type

FAQ

  1. vptr and vtbl(virutal function table and it's pointer): [refer]https://www.nowcoder.com/profile/3669004/test/13503903/22584#summary http://blog.csdn.net/haoel/article/details/1948051
    • When new an object, the compiler only allocates space for member variables while member functions are shared among objects belonging to same class.
    • The compiler adds _vptr to class automatically, and ensures contents of _vtbl at compile time, initializes _vptr in constructor of the class. _vptr at the begining of the class in memory. The objects of the class share the _vtbl of the class.
    • _vtbl is essentially an array of pointers that hold function pointers
    • In multi-inheritance, each base classes _vtbl stored in the derived class in defined order, and derived class shares the same _vptr as the first base class.
    • virtual functions declared in the derived class not only overwrite the virtual functions corresponding to each base class but also add to virtual function table of the first base class.
    • _vptr and _vtbl memory Layout: _vptr stored in the begining of the class momory in heap. _vtbl stored in .rodata segment. virtual functions themselves stored in .text segment.
    • Polymorphism process: base pointer(stack) ==> _vptr(heap) ==> _vtbl(.rodata) ==> .text
  2. Scope(::) operator purpose:
    • To access the global variable when there is a local variale with the same name.
    • To define functions outside the class.
    • To access class's staic variable.
    • In case of the multiple Inheritance.
  3. Single, multiple and virtual inheritance:

Atomic

Synchronization with Atomics in C++20

  • Atomics guarantee two characteristics:🔗 Ref
    • they are atomic
    • they provide synchronisation and order constraints on the program execution.

std::memory_order

memory_order_relaxed

  • There are no synchronization or ordering constraints imposed on other reads or writes, only this operation's atomicity is guaranteed

memory_order_consume

  • A load operation with this memory order performs a consume operation on the affected memory location:
    • no reads or writes in the current thread dependent on the value currently loaded can be reordered before this load.
  • Writes to data-dependent variables in other threads that release the same atomic variable are visible in the current thread. On most platforms, this affects compiler optimizations only.
  • The std::memory_order_consume is about data dependencies on atomics. Both dependencies introduce a happens-before relation. 🔗 Ref
    • carries-a-dependency-to: If the result of an operation A is used as an operand of an operation B, then: A carries-a-dependency-to B.
    • dependecy-ordered-before: A store operation (with std::memory_order_release, std::memory_order_acq_rel or std::memory_order_seq_cst), is dependency-ordered-before a load operation B (with std::memory_order_consume), if the result of the load operation B is used in a further operation C in the same thread. The operations B and C have to be in the same thread.

memory_order_acquire

  • A load operation with this memory order performs the acquire operation on the affected memory location:
    • no reads or writes in the current thread can be reordered before this load.
  • All writes in other threads that release the same atomic variable are visible in the current thread
  • Acquire semantics prevent memory reordering of the read-acquire with any read or write operation that follows it in program order.

memory_order_release

  • A store operation with this memory order performs the release operation:
    • no reads or writes in the current thread can be reordered after this store.
  • All writes in the current thread are visible in other threads that acquire the same atomic variable and writes that carry a dependency into the atomic variable become visible in other threads that consume the same atomic
  • Release semantics prevent memory reordering of the write-release with any read or write operation that precedes it in program order.

memory_order_acq_rel

  • A read-modify-write operation with this memory order is both an acquire operation and a release operation.
  • No memory reads or writes in the current thread can be reordered before or after this store.
  • All writes in other threads that release the same atomic variable are visible before the modification and the modification is visible in other threads that acquire the same atomic variable.
  • The acquire-release semantic is the key for a deeper understanding of the multithreading programming because the threads will be synchronised at specific synchronisation points in the code. Without these synchronisation points, there is no well-defined behaviour of threads, tasks or condition variables possible. 🔗 Ref
  • The acquire-release semantic is based on one key idea: 🔗 Ref
    • A release operation synchronises with an acquire operation on the same atomic and establishes an ordering constraint.
    • So, all read and write operations can not be moved before an acquire operation, all read and write operations can not be move behind a release operation.

memory_order_seq_cst

  • A load operation with this memory order performs an acquire operation, a store performs a release operation, and read-modify-write performs both an acquire operation and a release operation, plus a single total order exists in which all threads observe all modifications in the same order
  • Sequential consistency provides two guarantees. 🔗 Ref
    • The instructions of a program are executed in source code order.
    • There is a global order of all operations on all threads.

Reference:

std::atomic_flag

  • Two very interesting properties:
    • the only lock-free atomic.
    • the building block for higher thread abstractions.

Condition Variable

Synchronization with Atomics in C++20

Cons

  • Lost wakeup:

    • The sender sends its notification before the receiver gets to its wait state. The consequence is that the notification is lost.
    • The C++ standard describes condition variables as a simultaneous synchronisation mechanism: "The condition_variable class is a synchronisation primitive that can be used to block a thread, or multiple threads at the same time, ...". So the notification gets lost, and the receiver is waiting and waiting and ... .
  • Spurious wakeup:

    • Usually happen because, in between the time when the condition variable was signaled and when the waiting thread finally ran, another thread ran and changed the condition. There was a race condition between the threads, with the typical result that sometimes, the thread waking up on the condition variable runs first, winning the race, and sometimes it runs second, losing the race. At a minimum POSIX Threads and the Windows API can be victims of these phenomena.
    • Some implementations (in particular many implementations of pthreads) recognize this situation and avoid this "hurry up and wait" scenario by transferring the waiting thread from the condition variable's queue directly to the queue of the mutex within the notify call, without waking it up
  • The predicate protects against both flaws:

    • Lost wakeup: the thread checks the predicate when it is going to sleep. If it's ture, will not go to sleep.
    • Spurious wakeup: if the predicate is false, continue waiting.

The notification would be lost when the sender sends its notification before the receiver is in the wait state and does not use a predicate. Consequently, the receiver waits for something that never happens. This is a deadlock.

C++ Core Guidelines: Be Aware of the Traps of Condition Variables

When you only need a one-time notification such as in the previous program, promises and futures are a better choice than condition variables. Promise and futures cannot be victims of spurious or lost wakeups.

Promises and Futures

Synchronization with Atomics in C++20

Thread Synchronization with Condition Variables or Tasks

Criteria Condition Variables Task (Promise - Future)
Multiple synchronization Possible Y N
Critical region Y N
Exception handling in receiver N Y
Spurious wakeup Y N
Lost wakeup Y N

The benefit of a condition variable to a promise and future is, that you can use condition variables to synchronize threads multiple times. In opposite to that, a promise can send its notification only once.

Cons

There is only one downside to using promises and futures: they can only be used once. If you want to communicate more than once, you have to use condition variables or atomics.

CRTP

CRTP

  • The main usage of the CRTP is to implementing:

    • static polymorphism
    • static interface
    • adding functionality to original class
  • In general, a class deriving from another class expresses that the derived class somehow conceptually "is a" base class. The purpose is to use the base class in generic code, and to redirect calls to the base class over to code in the derived class.

  • With the CRTP the situation is radically different. The derived class does not express the fact it "is a" base class. Rather, it expands its interface by inherting from the base class, in order to add more functionality.

  • Therefore the base class is not the interface, and the derived class is not the implementation

  • Drawbacks:

    1. Derivec class may hide the base class function if they have the same function name (can solve by mixin class)
    2. There is a level of indirection that is inherent to the CRTP: have to implement functionality in the base and derived classes (cal solve by mixin class)
    3. It doesn’t clearly express the intention that it is constraining the API of CRTP (can solve by concepts) */
namespace crtp {
template <typename T>
struct crtp
{
    T& derive() { return static_cast<T&>(*this); }
    const T& derive() const { return static_cast<const T&>(*this); }
private:
    /* private constructor prevents the case: class Derived2 : public Base<Derived1> */
    crtp() = default;
    friend T;
};

/* phantom type is used to solve diamond inheritance problem */
template <typename T, template<typename> class crtpType>
struct crtp
{
    T& derive() { return static_cast<T&>(*this); }
    const T& derive() const { return static_cast<const T&>(*this); }
private:
    crtp() = default;
    friend crtpType<T>;
};


template<typename Printable>
struct RepeatPrint
{
    void repeat(unsigned int n) const {
        while (n-- > 0) {
            static_cast<Printable const&>(*this).print();
        }
    }
};


class Name : public RepeatPrint<Name>
{
public:
    Name(std::string firstName, std::string lastName)
    :   firstName_(std::move(firstName)),
        lastName_(std::move(lastName))
    {}

    void print() const {
        std::cout << lastName_ << ", " << firstName_ << '\n';
    }

private:
    std::string firstName_;
    std::string lastName_;
};

int main() {
    Name ned("Eddard", "Stark");
    ned.repeat(10);
}
} // namspace crtp

mixin

  • Mixin classes are template classes that define a generic behaviour, and are designed to inherit from the type you wish to plug their functionality onto.

  • The idea of the mixin class is to isolate the generic functionality into its own class, template this class on the type we want to plug in onto, and derive from that type.

  • Mixin classes involve a mix of template and inheritance in order to plug a generic functionality onto an existing class.

  • Mixin classes are like the CRTP, but upside down

namespace mixin {
class Name {
public:
    Name(std::string firstName, std::string lastName)
    :   firstName_(std::move(firstName)),
        lastName_(std::move(lastName))
    {}

    void print() const {
        std::cout << lastName_ << ", " << firstName_ << '\n';
    }

private:
    std::string firstName_;
    std::string lastName_;
};

template<typename Printable>
struct RepeatPrint : Printable {
    explicit RepeatPrint(Printable const& printable) : Printable(printable) {}

    void repeat(unsigned int n) const {
        while (n-- > 0) {
            this->print();
        }
    }
};

int main() {
    Name ned("Eddard", "Stark");
    RepeatPrint<Name>(ned).repeat(10);
}

} // namespace mixin

Compare CRTP with mixin

  • CRTP and mixin classes provide two approaches to the same problem:

    • adding a generic functionality to an existing class, but with different trade-offs.
  • Here are the points where they differ:

  • The CRTP:

    • impacts the definition of the original class, because it has to inherit from the CRTP,
    • client code uses the original class directly and benefits from its augmented functionalities.
  • The mixin class:

    • leaves the original class unchanged,
    • client code doesn’t use the original class directly, it needs to wrap it into the mixin to use the augmented functionality,
    • inherits from a the original class even if it doesn’t have a virtual destructor. This is ok unless the mixin class is deleted polymorphically through a pointer to the original class.

Reference: