Movatterモバイル変換


[0]ホーム

URL:




Chapter 22: Class Templates

Templates can not only be constructed for functions but also for completeclasses. Consider constructing aclass template when a class should be ableto handle different types of data. Class templates are frequently used inC++: chapter12 discusses data structures likevector,stack andqueue, that are implemented asclass templates. With classtemplates, the algorithms and the data on which the algorithms operate arecompletely separated from each other. To use a particular data structure incombination with a particular data type only the data type needs to bespecified when defining or declaring a class template object (as instack<int> iStack).

In this chapter constructing and using class templates is discussed. In asense, class templates compete with object oriented programming (cf. chapter14), that uses a mechanism resembling that oftemplates. Polymorphism allows the programmer to postpone the implementationof algorithms by deriving classes from base classes in which algorithms areonly partially implemented. The actual definition and processing of the dataupon which the algorithms operate may be postponed until derived classes aredefined. Likewise, templates allow the programmer to postpone thespecification of the data upon which the algorithms operate. This is mostclearly seen with abstract containers, which completely specify the algorithmsand at the same time leave the data type on which the algorithms operatecompletely unspecified.

There exists an intriguing correspondence between the kind of polymorphismwe've encountered in chapter14 and certain uses of classtemplates. In their bookC++ Coding Standards (Addison-Wesley, 2005) Sutter and Alexandrescu refer tostatic polymorphism anddynamic polymorphism.Dynamic polymorphism is what we use when overriding virtual members.Usingvtables the function that is actually called depends on the type ofobject a (base) class pointer points at.Static polymorphism isencountered in the context of templates, and is discussed and compared todynamic polymorphism in section22.12.

Generally, class templates are easier to use than polymorphism. It iscertainly easier to writestack<int> istack to create a stack ofintsthan to derive a new classIstack: public stack and to implement allnecessary member functions defining a similar stack ofints using objectoriented programming. On the other hand, for each different type for which anobject of a class template is defined another, possibly complete class must bereinstantiated. This is not required in the context of object orientedprogramming where derived classesuse, rather thancopy, the functionsthat are already available in their base classes (but see also section22.11).

Previously we've already used class templates. Objects likevector<int> viandvector<string> vs are commonly used. The data types for which thesetemplates are defined and instantiated are an inherent part of such containertypes. It is stressed that it is thecombination of a class template typeand its template parameter(s), rather than the mere class template's typethat defines or generates a type. Sovector<int> is a type as isvector<string>. Such types could very well be specified throughusing-declarations:

    using IntVector =  std::vector<int>;    using StringVector = std::vector<std::string>;    IntVector vi;    StringVector vs;

Like function templates, class templates, both the class interface and theimplementations of the class member functions, must be available at theirpoints of instantiation. Therefore the interfaces and implementations of classtemplates are commonly provided in a header file, which is then included bysource files using those classes (see also section21.13).

22.0.1: Template Argument Deduction

One important difference between function templates and class templates isthat the template arguments of function templates can normally be deduced bythe compiler, whereas template arguments of class templates may have to be specified by the user. That's not always required, but since the templatetypes of class templates are an inherent part of the class template's type itmust be completely clear to the compiler what the class template's types are.

So, in case we have a

    template <typename T>    void fun(T const &param);

we can do

    vector<int> vi;    fun(vi)

and the compiler deducesT == vector<int>.

On the other hand, if we have

    template <typename T>    struct Fun    {        T d_data;        Fun();    };

we cannot do

    Fun fun;

sinceFun is not a type, and the compiler cannot deduce what the intendedtype is.

Sometimes the compilercan deduce the intended types. Consider this:

    vector vi{ 1, 2, 3, 4 };

In this case the compiler deducesint from the provided values. Thecompiler is smart enought to select the most general type, as in this example,wheredouble is deduced:

    vector vi{ 1, 2.5 };

The compiler is doing its utmost to deduce types when they're notexplicitly specified, and it will stay on the safe side. So the vector is avector<int> in the first example and avector<double> in the second.

Although it's nice that the compiler is willing and able to deduce types inmany situations it's at the same time a potential source of confusion. In thefirst example only non-negative values are used, so why not define avector<unsigned> or avector<size_t>? In the second example thecompiler deducesvector<double>, butvector<float> could very wellalso have been used.

To avoid confusion, as arule of thumb, it might be a good idea to specifythe types of class templates when defining class type objects. It requireshardly any effort and it completely clarifies what you want and mean. Soprefer

    vector<int>     vi{ 1, 2, 3 };    vector<double>  vd{ 1, 2.5 };

over the definitions implicitly using types.

22.0.1.1: Simple Definitions

Here are some examples of how simple definitions where the compiler deducestemplate arguments can (and cannot) be specified.

Starting point:

    template <class ...Types>           // any set of types    class Deduce    {        public:            Deduce(Types ...params);    // constructors            void fun();                 // a member function    };

Some definitions:

                                    // deduce:     full definition:                                    // --------------------------------    Deduce first{1};                // 1: int   -> Deduce<int> first{1}     Deduce second;                  // no Types -> Deduce<> second;      Deduce &&ref = Deduce<int>{ 1 };// int      -> Deduce<int> &&ref    template <class Type>    Deduce third{ static_cast<Type *>(0) };

The templatethird is a recipe for constructingDeduceobjects from a type that's specified forthird. The pointer's type simply is a pointer to the specified type (so, specifyingthird<int> implies anint *). Now that the type ofthird's argument is available (i.e., anint *) the compiler deduces thatthird{0} is aDeduce<int *>.

ThisDeduce<int *> object could, e.g., be used to initialize a namedDeduce<int *> object:

    auto x = third<int>;        // x's full definition: Deduce<int *> x{0}

Deduce's member functions can be used by anonymous and named objects:

    x.fun();                    // OK: fun called by named object    third<int>.fun();           // OK: fun called by anonymous object

Here are some examples that won't compile:

    extern Deduce object;       // not an object definition    Deduce *pointer = 0;        // any set of types could be intended    Deduce function();          // same.

When defining objects, either using function-type or curly brace definitionstemplate argument deduction is realized as follows:

Let's apply this process to the classDeduce. The set of imaginaryfunctions matchingDeduce looks like this:

                                        // already encountered: matching    template <class ...Types>           // Deduce(Types ...params)    Deduce<Types ...> imaginary(Types ...params);                                        // the copy constructor: matching    template <class ...Types>           // Deduce(Deduce<Types ...> const &)    Deduce<Types ...> imaginary(Deduce<Types ...> const &other);                                                                            // the move constructor, matching    template <class ...Types>           // Deduce(Deduce<Types ...> &&)    Deduce<Types ...> imaginary(Deduce<Types ...> &&tmp);

For the constructionDeduce first{1} the first imaginary function winsthe overload contest, resulting in the template argument deductionint forclass ...Types, and henceDeduce<int> first{1} is defined.

Note that when a class template is nested under a class template, the nestedclass template's name depends on the outer class type. The outer class thenprovides the name qualifier for the inner class template. In such casestemplate argument deduction is used for the nested class, but (as it is notused for name qualifiers) is not used for the outer class. Here is an example:adding a nested class template toOuter:

    template <class OuterType>    class Outer    {        public:            template <class InnerType>            struct Inner            {                Inner(OuterType);                Inner(OuterType, InnerType);                template <typename ExtraType>                Inner(ExtraType, InnerType);            };    };    // defining:    Outer<int>::Inner inner{2.0, 1};

In this case, the compiler uses these imaginary functions:

    template <typename InnerType>    Outer<int>::Inner<InnerType>            // copy constructor        imaginary(Outer<int>::Inner<InnerType> const &);    template <typename InnerType>           Outer<int>::Inner<InnerType>            // move constructor        imaginary(Outer<int>::Inner<InnerType> &&);    template <typename InnerType>           // first declared constructor    Outer<int>::Inner<InnerType> imaginary(int);    template <typename InnerType>           // second declared constructor    Outer<int>::Inner<InnerType> imaginary(int, InnerType);    template <typename InnerType>           // third declared constructor    template <typename ExtraType>           Outer<int>::Inner<InnerType> imaginary(ExtraType, InnerType);

Template argument deduction for callingimaginary(2.0, 1) results indouble for its first argument andint for its second. Overloadresolution then favors the last imaginary function, and soExtraType:double andInnerType: int. Consequently,

    Outer<int>::Inner inner{ 2.0, 1 };

is defined as:

    Outer<int>::Inner<int> Inner{ 2.0, 1 };

22.0.1.2: Explicit Conversions

Have a look at the following class interface:
    template <class T>    struct Class    {        std::vector<T> d_data;        struct Iterator        {            using type = T;            bool operator!=(Iterator const &rhs);            Iterator operator++(int);            type const &operator*() const;            // ...         };            Class();        Class(T t);        Class(Iterator begin, Iterator end);            template <class Tp>        Class(Tp first, Tp second)        {}        Iterator begin();        Iterator end();    };

The implementation of theClass constructor expecting twoClass::Iterator arguments would probably be somewhat similar to thefollowing:

    template <class T>    Class<T>::Class(Iterator begin, Iterator end)    {        while (begin != end)            d_data.push_back(*begin++);    }

whered_data is some container storingT values. AClass object can now constructed from a pair ofClass::Iterators:

    Class<int> source;          ...    Class<int> dest{source.begin(), source.end()};

Here, the simple template argument deduction procedure fails to deduce theint template argument. This fails:

    Class dest{source.begin(), source.end()};

When attempting to instantiate aClass object by passing itClass::Iterators the compiler cannot directly deduce from the providedarguments that aClass<Class::Iterator::type> is to be used:typeisn't directly available. Compare this toClass's third constructor,where

    Class intObject{12};

allows the compiler to create an imaginary function

    template <class Type>    Class <Type> imaginary(Type param)

in which caseType clearly is anint, and so aClass<int>object is constructed.

When we try to do this forClass(Iterator, Iterator) we get

    template <class Iterator>    Class<???> imaginary(Iterator, Iterator);

and hereClass's template argument isn't directly related toIterator: the compiler cannot deduce its type and consequently compilationfails.

A similar argument applies to the fourth constructor, which receives twoTparguments, which are both independent fromClass's template type.

In cases like these simple type template argument deduction procedurefails. Still, not everything is lost:explicit conversions, defined asexplicitly specifieddeduction rules which are added (below) to theclass's interface can be used.

An explicitly specified deduction rule connects a class template constructorsignature to a class template type. It specifies the template arguments forthe class template object that is constructed using the constructor whosesignature is specified. The generic syntactical form of an explicitlyspecified deduction rule looks like this:

    class template constructor signature -> resulting class type ;

Let's apply this toClass(Iterator begin, Iterator end). Its signature is

    template <class Iterator>    Class(Iterator begin, Iterator end)

Requiring thatIterator defines a typenametype, we can now formulatea resulting class type:

    Class<typename Iterator::type>

Now we can combine both in an explicitly specified deduction rule (which isadded as a separate line belowClass's interface):

    template <class Iterator>    Class(Iterator begin, Iterator end) -> Class<typename Iterator::type>;

After adding this deduction rule toClass's interface the followingconstructor calls successfully compile:

    Class src{12};      // already OK    Class dest1{src.begin(), src.end()};                        // begin() and end() return Class<int>::Iterator                        // objects. Typename Class<int>::Iterator::type                        // is defined as int, so Class<int> dest1 is                        // defined.    struct Double       // used at the next construction    {        using type = double;        // ... members ...    };    Class dest2{Double{}, Double{}};                        // Here the struct Double defines                         // typename double type, so                        // Class<double> dest2 is defined.

Within classes the compiler uses (as before) the class itself when merelyreferring to the class name: when referring toClass in the classClass, the compiler considersClass to beClass<T>. So the headersof the declaration and definition ofClass's copy constructor look likethis:

    Class(Class const &other);      // declaration    template <class T>              // definition    Class<T>::Class(Class const &other)    {         // ...     }

Sometimes the default type is not what you want, in which case the requiredtype must explicitly be specified. Consider what happens if add amemberdup toClass:

    template <typename T>    template <typename Tp>    auto Class<T>::dup(Tp first, Tp second)    {        return Class{ first, second };  // probably not what you want    }                                   // (see the following text)

Here, because we're insideClass the compiler deduces thatClass<T> must be returned. But in the previous section we decided that,when initializingClass from iterators,Class<typename Tp::type>should be constructed and returned. To accomplish that, the required type isexplicitly specified:

    template <typename T>    template <typename Tp>    auto Class<T>::dup(Tp first, Tp second)    {                               // OK: explicitly specified Class tyoe.        return Class<typename Tp::type>{ first, second };    }

As shown in this example, simple (implicit) or explicit deduction rules do nothave to be used: theycan be used in many standard situations whereexplicitly specifying the class's template arguments appears to besuperfluous. Template argument deduction was added to the language to simplifyobject construction when using class templates. But in the end you don'thave to use these deduction rules: it's always still possible toexplicitly specify template arguments.

22.1: Defining class templates

Having covered the construction of function templates, we're now ready for thenext step: constructing class templates. Many useful class templates alreadyexist. Rather than illustrating the construction of a class template bylooking at an already existing class template the construction of anotherpotentially useful new class template will be undertaken.

The new class implements acircular queue. A circular queue has a fixednumber ofmax_size elements. New elements are inserted at its back andonly its head and tail elements can be accessed. Only the head element can beremoved from a circular queue. Oncen elements have been appended the nextelement is inserted again at the queue's (physical) first position. Thecircular queue allows insertions until it holdsmax_size elements. As longas a circular queue contains at least one element elements may be removed fromit. Trying to remove an element from an empty circular queue or to add anotherelement to a full circular queue results in exceptions being thrown. Inaddition to other constructors a circular queue must offer a constructorinitializing its objects formax_size elements. This constructor must makeavailable the memory for themax_size elements but must not call thoseelements default constructors (hinting at the use of the placementnewoperator). A circular queue should offer value semantics as well as a moveconstructor.

Please note that in the above description the actual data type that is usedfor the circular queue is nowhere mentioned. This is a clear indication thatour class could very well be defined as aclass template. Alternatively, the class could bedefined for some concrete data type which is then abstracted when convertingthe class to a class template.

The actual construction of a class template is provided in the next section,where the class templateCirQue (circular queue) is developed.

22.1.1: Constructing the circular queue: CirQue

The construction of a class template is illustrated in this section. Here,we'll develop the class templateCirQue (circular queue). This classtemplate has one template type parameter,Data, representing the data typethat is stored in the circular queue. The outline of the interface of thisclass template looks like this:
    template<typename Data>    class CirQue    {        // member declarations    };

A class template's definition starts like a function template'sdefinition:

Once theCirQue class template has been defined it can be used tocreate all kinds of circular queues. As one of its constructors expects asize_t argument defining the maximum number of elements that can be storedin the circular queue, circular queues could be defined like this:
    CirQue<int> cqi(10);            // max 10 ints    CirQue<std::string> cqstr(30);  // max 30 strings

As noted in the introductory section of this chapter the combination ofname of the class template and the data type for which it is instantiateddefines a data type. Also note the similarity between defining astd::vector (of some data type) and aCirQue (of some data type).

Likestd::map containers class templates may be defined with multipletemplate type parameters.

Back toCirQue. ACirQue must be capable of storingmax_sizeData elements. These elements are eventually stored in memory pointed atby a pointerData *d_data, initially pointing to raw memory. New elementsare added at the backside of theCirQue. A pointerData *d_back isused to point to the location where the next element is going to bestored. Likewise,Data *d_front points to the location of theCirQue'sfirst element. Twosize_t data members are used to monitor the fillingstate of theCirQue:d_size represents the number of elementscurrently stored in theCirQue,d_maxSize represents the maximumnumber of elements that theCirQue can contain. Thus, theCirQue'sdata members are:

    size_t d_size;    size_t d_maxSize;    Data *d_data;    Data *d_front;    Data *d_back;

22.1.2: Non-type parameters

Function template parameters are either template type parameters or templatenon-type parameters (actually, a third type of template parameter exists, thetemplate template parameter, which is discussed in chapter23 (section23.4)).

Class templates may also definenon-type parameters. Like the function template non-type parameters they mustbe (integral) constants whose values must be known at object instantiationtime.

Different from function template non-type parameters the values of classtemplate non-type parameters arenot deduced by the compiler usingarguments passed to class template members.

Assume we extend our design of the class templateCirQue so that itdefines a second (non-type) parametersize_t Size. Our intent is to usethisSize parameter in the constructor defining an array parameter ofSize elements of typeData.

TheCirQue class template now becomes (only showing the relevantconstructor):

    template <typename Data, size_t Size>    class CirQue    {        // ... data members        public:            CirQue(Data const (&arr)[Size]);            ...    };    template <typename Data, size_t Size>    CirQue<Data, Size>::CirQue(Data const (&arr)[Size])    :        d_maxSize(Size),        d_size(0),        d_data(operator new(Size * sizeof(Data))),        d_front(d_data),        d_back(d_data),    {        std::copy(arr, arr + Size, back_inserter(*this));    }

Unfortunately, this setup doesn't satisfy our needs as the values oftemplate non-type parameters are not deduced by the compiler. When thecompiler is asked to compile the followingmain function it reports amismatch between the required and actual number of template parameters:

    int main()    {        int arr[30];        CirQue<int> ap{ arr };    }    /*        Error reported by the compiler:        In function `int main()':            error: wrong number of template arguments (1, should be 2)            error: provided for `template<class Data, size_t Size>                   class CirQue'    */

DefiningSize as a non-type parameter having a default value doesn'twork either. The compiler always uses the default unless its value isexplicitly specified. Reasoning thatSize can be 0 unless we need anothervalue, we might be tempted to specifysize_t Size = 0 in the template'sparameter type list. Doing so we create a mismatch between the defaultvalue 0 and the actual size of the arrayarr as defined in the abovemain function. The compiler, using the default value, reports:

    In instantiation of `CirQue<int, 0>':    ...    error: creating array with size zero (`0')

So, although class templates may use non-type parameters they must alwaysbe specified like type parameters when an object of that class isdefined. Default values can be specified for those non-type parameters causingthe compiler to use the default when the non-type parameter is leftunspecified.

Default template parameter values(either type or non-type template parameters) maynot be specified whendefining template member functions. In general: function template definitions(and thus: class template member functions) may not be given default template(non) type arguments. If default template arguments are to be used for classtemplate members, theyhave to be specified by the class interface.

Similar to non-type parameters of function templates default argumentvalues for non-type class template parameters may only be specified asconstants:

Although our attempts to define a constructor of the classCirQueaccepting an array as its argument has failed so far, we're not yet out ofoptions. In the next section a method is described thatdoes allow usto reach our goal.

22.1.3: Member templates

Our previous attempt to define a template non-type parameter that isinitialized by the compiler to the number of elements of an array failedbecause the template's parameters are not implicitly deduced when aconstructor is called. Instead, they must explicitly be specified when anobject of the class template is defined. As the template arguments arespecified just before the template's constructor is called the compilerdoesn't have to deduce anything and it can simply use the explicitly specifiedtemplate arguments.

In contrast, whenfunction templates are used, the actual templateparameters are deduced from the arguments used when calling the function. Thisopens up an alley leading to the solution of our problem. If the constructoritself is turned into a function template (having its own template header),then the compilerwill be able to deduce the non-type parameter's valueand there is no need anymore to specify it explicitly using a class templatenon-type parameter.

Members (functions or nested classes) of class templates that are themselvestemplates are calledmember templates.

Member templates are defined as any other template, including its own templateheader.

When converting our earlierCirQue(Data const (&array)[Size]) constructorinto a member template the class template'sData type parameter can stillbe used, but we must provide the member template with a non-type parameter ofits own. Its declaration in the (partially shown) class interface looks likethis:

    template <typename Data>    class CirQue    {        public:            template <size_t Size>            explicit CirQue(Data const (&arr)[Size]);    };

Its implementation becomes:

    template <typename Data>    template <size_t Size>    CirQue<Data>::CirQue(Data const (&arr)[Size])    :        d_size(0),        d_maxSize(Size),        d_data(static_cast<Data *>(operator new(sizeof(arr)))),        d_front(d_data),        d_back(d_data)    {        std::copy(arr, arr + Size, std::back_inserter(*this));    }
The implementation uses the STL'scopy algorithm and aback_inserter adapter to insert the array's elements into theCirQue. To use theback_inserterCirQue must provide two (public)using-declarations (cf. section18.2.2):
    using value_type = Data;    using const_reference = value_type const &;

Member templates have the following characteristics:

A potentially occurring problem remains. Assume that in addition to theabove member template aCirQue<Data>::CirQue(Data const *data) has beendefined. Some (here not further elaborated) protocol could be defined allowingthe constructor to determine the number of elements that should be stored intheCirQue object. When we now define

    CirQue<int> object{ array };

it is this latter constructor, rather than the member template, that thecompiler will use.

The compiler selects this latter constructor as it is a more specializedversion of a constructor of the classCirQue than the member template(cf. section21.14). Problems like these can be solved bydefining the constructorCirQue(Data const *data) into a member templateas well or by defining a constructor using two parameters, the secondparameter defining the number of elements to copy.

When using the former constructor (i.e., the member template) it must define atemplate type parameterData2. Here `Data' cannot be used as templateparameters of a member template may not shadow template parameters of itsclass. UsingData2 instead ofData takes care of this subtlety. Thefollowing declaration of the constructorCirQue(Data2 const *) couldappear inCirQue's header file:

    template <typename Data>    class CirQue    {        template <typename Data2>        explicit CirQue(Data2 const *data);    }

Here is how the two constructors are selected in code defining twoCirQue objects:

    int main()    {        int array[30];        int *iPtr = array;        CirQue<int> ac{ array };    // calls CirQue(Data const (&arr)[Size])        CirQue<int> acPtr{ iPtr };  // calls CirQue(Data2 const *)    }

22.1.4: CirQue's constructors and member functions

It's time to return toCirque's design and construction again.

The classCirQue offers various member functions. Normal designprinciples should be adhered to when constructing class templatemembers. Class template type parameters should preferably be defined asType const &, rather thanType, to prevent unnecessary copying oflarge data structures. Template class constructors should use memberinitializers rather than member assignment within the body of theconstructors. Member function definitions should preferably not be providedin-class but below the class interface. Since class template member functionsare function templates their definitions should be provided in the headerfile offering the class interface. Theymay be given theinlineattribute.

CirQue declares several constructors and (public) members (theirdefinitions are provided as well; all definitions are provided below the classinterface).

Here are the constructors and the destructor:

Here areCirque's members:

The remaining public members all consist of one-liners and wereimplemented as inline function templates:

Finally, the class has one private member,inc, returning acyclically incremented pointer intoCirQue's raw memory:

    template<typename Data>    Data *CirQue<Data>::inc(Data *ptr)    {        ++ptr;        return ptr == d_data + d_maxSize ? d_data : ptr;    }

22.1.5: Using CirQue objects

When objects of a class template are instantiated,only thedefinitions of all the template's member functions that are actually used musthave been seen by the compiler.

That characteristic of templates could be refined to the point where eachdefinition is stored in a separate function template definition file. In thatcase only the definitions of the function templates that are actually neededwould have to be included. However, it is hardly ever done that way. Instead,the usual way to define class templates is to define the interface and todefine the remaining function templates immediately below the class template'sinterface (defining some functions inline).

Now that the classCirQue has been defined, it can be used. To use theclass its object must be instantiated for a particular data type. In thefollowing example it is initialized for data typestd::string:

#include "cirque.h"#include <iostream>#include <string>using namespace std;int main(){    CirQue<string> ci(4);    ci.push_back("1");    ci.push_back("2");    cout << ci.size() << ' ' << ci.front() << ' ' << ci.back() << '\n';    ci.push_back("3");    ci.pop_front();    ci.push_back("4");    ci.pop_front();    ci.push_back("5");    cout << ci.size() << ' ' << ci.front() << ' ' << ci.back() << '\n';    CirQue<string> copy(ci);    copy.pop_front();    cout << copy.size() << ' ' << copy.front() << ' ' << copy.back() << '\n';    int arr[] = {1, 3, 5, 7, 9};    CirQue<int> ca(arr);    cout << ca.size() << ' ' << ca.front() << ' ' << ca.back() << '\n';//    int *ap = arr;//    CirQue<int> cap(ap);}
This program produces the following output:
    2 1 2    3 3 5    2 4 5    5 1 9

22.1.6: Default class template parameters

Different from function templates, template parameters of template classes maybe given default argument values. This holds true for both template type- and template non-typeparameters. If default template arguments were defined and if a class templateobject is instantiated without specifying arguments for its templateparameters then the template parameter's defaults are used.

When defining defaults keep in mind that they should be suitable for themajority of instantiations of the class. E.g., for the class templateCirQue the template's type parameter list could have been alteredby specifyingint as its default type:

    template <typename Data = int>

Even though default arguments can be specified, the compiler must still beinformed that object definitions refer to templates. When instantiating classtemplate objects using default template arguments the type specifications maybe omitted but the angle brackets must be retained. Assuming a defaulttype for theCirQue class, an object of that class may be definedas:

    CirQue<> intCirQue(10);

Default template arguments cannot be specified when defining templatemembers. So, the definition of, e.g., thepush_back member must alwaysbegin with the sametemplate specification:

    template <typename Data>

When a class template uses multiple template parameters, all may be givendefault values. Like default function arguments, once a default value is usedall remaining template parameters must also use their default values. Atemplate type specification list may not start with a comma, nor may itcontain multiple consecutive commas.

22.1.7: Declaring class templates

Class templates may also bedeclared. This may be useful insituations where forward class declarations are required. To declare a class template, simply remove its interface (the part between the curlybraces):
    template <typename Data>    class CirQue;

Default template arguments may alsobe specified when declaring class templates. However, default templatearguments cannot be specified for both the declaration and the definition of aclass template. As arule of thumb default template arguments should beomitted fromdeclarations, as class template declarations are never usedwhen instantiating objects but are only occasionally used as forwardreferences. Note that this differs from default parameter value specificationsfor member functions in ordinary classes. Such defaults are always specifiedwhen declaring the member functions in the class interface.

22.1.8: Preventing template instantiations

InC++ templates are instantiated when the address of a function template or class templateobject is taken or when a function template or class template is used. Asdescribed in section22.1.7 it is possible to (forward) declare aclass template to allow the definition of a pointer or reference tothat template class or to allow it being used as a return type.

In other situations templates are instantiated when they are being used. Ifthis happens many times (i.e., in many different source files) then this mayslow down the compilation process considerably. Fortunately,C++ allowsprogrammers toprevent templates from being instantiated, using theextern template syntax. Example:

    extern template class std::vector<int>;

Having declared the class template it can be used in its translationunit. E.g., the following function properly compiles:

#include <vector>#include <iostream>using namespace std;extern template class vector<int>;void vectorUser(){    vector<int> vi;    cout << vi.size() << '\n';}
But be careful:

22.1.9: Generic lambda expressions

Generic lambda expressions may useauto to definetheir parameters. When used, a lambda expression is instantiated by looking atthe actual types of arguments. Sinceauto is generic, different parametersdefined usingauto can be instantiated to different types. Here is anexample (assuming all required headers and namespace declaration werespecified):
     1: int main()     2: {     3:     auto lambda = [](auto lhs, auto rhs)     4:                 {     5:                     return lhs + rhs;     6:                 };     7:      8:     vector<int> values {1, 2, 3, 4, 5};     9:     vector<string> text {"a", "b", "c", "d", "e"};    10:     11:     cout <<    12:         accumulate(values.begin(), values.end(), 0, lambda) << '\n' <<    13:         accumulate(text.begin(), text.end(), string{}, lambda) << '\n';    14: }
The generic lambda function is defined in lines 3 through 6, and isassigned to thelambda identifier. Then,lambda is passed toaccumulate in lines 12 and 13. In line 12 it is instantiated to addint values, in line 13 to addstd::string values: the samelambdais instantiated to two completely different functors, which are only locallyavailable inmain.

Generic lambda expressions are in fact class templates. To illustrate: theabove example of a generic lambda expression could also have been implementedusing the following class template:

    struct Lambda    {        template <typename LHS, typename RHS>        auto operator()(LHS const &lhs, RHS const &rhs) const         {            return lhs + rhs;        }    };    auto lambda = Lambda{};

This identity implies that usingauto in the lambda expression'sparameter list obeys the rules of template argument deduction (cf. section21.4), which are somewhat different from the wayauto normallyoperates.

Another extension is the way generic lambda expressions capture outer scopevariables. Previously variables could only be captured by value or byreference. As a consequence an outer scope variable of a type that onlysupports move construction could not be passed by value to a lambdafunction. This restriction was dropped, allowing variables to be initializedfrom arbitrary expressions. This not only allows move-initialization ofvariables in the lambda introducer, but with generic lambdas variables mayalso be initialized if they do not have a correspondingly named variable inthe lambda expression's outer scope. In this case initializer expressions canbe used as follows:

    auto fun = [value = 1]                {                   return value;               };

This lambda function (of course) returns 1: the declared capture deducesthe type from the initializer expression as ifauto had been used.

To use move-initializationstd::move should be used. E.g., aunique_ptr only supports move-assignment. Here it is used to return one ofits values via a lambda function:

    std::unique_ptr<int> ptr(new int(10));    auto fun = [value = std::move(ptr)]                {                   return *value;               };

In generic lambda expressions the keywordauto indicates that the compilerdetermines which types to use when the lambda function is instantiated. Ageneric lambda expression thereforeis a class template, even though itdoesn't look like one. As an example, the following lambda expression definesa generic class template, which can be used as shown:

    char const *target = "hello";    auto lambda =                [target](auto const &str)       {           return str == target;       };           vector<string> vs{stringVectorFactory()};    find_if(vs.begin(), vs.end(), lambda);

This works fine, but if you definelambda this way then you should beprepared for complex error messages if the types of the derefenced iteratorsand lambda's (silently assumed)str type don't match.

Here is a little program illustrating how generic lambda expressions can beused in other generic lambda expressions: class templates could also have beenused. In lines 1 through 9 a generic lambda expressionaccumulate isdefined, defining a second paramater which is used as a function: its argumenttherefore should be usable as a function. A functor definitely is, an thesecond generic lambda expressionlambda, defined in lines 11 through 14provides it. In line 21accumulate andlambda are instantiated so thatthey operate on (a vector of)ints, in line 22 they are instantiated forprocessing (a vector of)strings:

     1: auto accumulate(auto const &container, auto function)     2: {     3:     auto accu = decltype(container[0]){};     4:      5:     for (auto &value: container)     6:         accu = function(accu, value);     7:      8:     return accu;     9: }    10:     11: auto lambda = [](auto lhs, auto rhs)    12:             {    13:                 return lhs + rhs;    14:             };    15:     16: int main()    17: {    18:     vector<int> values  = {1, 2, 3, 4, 5};    19:     vector<string> text = {"a", "b", "c", "d", "e"};    20:     21:     cout << accumulate(values,  lambda) << '\n' <<    22:             accumulate(text,    lambda) << '\n';    23: }

In some situations generic lambdas are a bit too generic, resulting in verboseimplementations which are not required by templates in general. Consider ageneric lambda that specifies anauto &it parameter, but in addition itshould specify a parametervalue of typeValueType that should bedefined byit's class. Such a parameter requires the use of adecltype(and maybe also the use ofstd::decay_t) to retrieveit's actualtype. Inside the lambda's body ausing declaration can be specified tomake the type available, but that again requires a verbose specification usingstd::decay_t anddecltype. Here is an example:

    auto generic =         [](auto &it,            typename std::decay_t<decltype(it)>::ValueType value)        {            using Type = std::decay_t<decltype(it)>;            typename Type::ValueType val2{ value };            Type::staticMember();        };

To avoid this kind of verbosity generic lambda functions can also be definedlike ordinary templates, in which case the template header immediately followsthe lambda-introducer. Using this alternative form the definition of thegeneric generic lambda simply and straightforwardly becomes:

    auto generic =         []<typename Type>(Type &it, typename Type::ValueType value)        {            typename Type::ValueType val2{ value };            Type::staticMember();        };

22.2: Static data members

Whenstatic members are defined in class templates, they are defined for every new type for which the class template isinstantiated. As they are staticmembers, there will only be one member per type for which the class templateis instantiated. For example, in a class like:
    template <typename Type>    class TheClass    {        static int s_objectCounter;    };

There will beoneTheClass<Type>::objectCounter for each differentType specification. The following object definitions result in theinstantiation of just one single static variable, shared among the twoobjects:

    TheClass<int> theClassOne;    TheClass<int> theClassTwo;

Mentioning static members in interfaces does not mean these members areactually defined. They are onlydeclared and must bedefinedseparately. With static members of class templates this is no different. Thedefinitions of static members are usually provided immediately following (i.e., below) the templateclass interface. For example, the static members_objectCounter's definition, positioned just below its class interface,looks like this:

    template <typename Type>                    // definition, following    int TheClass<Type>::s_objectCounter = 0;    // the interface

Heres_objectCounter is anint and is thus independent of thetemplate type parameterType. Multiple instantiations ofs_objectCounter for identicalTypes cause no problem, as the linkerwill remove all but one instantiation from the final executable (cf. section21.5).

In list-like constructions, where a pointer to objects of the classitself is required, the template type parameterType must be used whendefining the static variable. Example:

    template <typename Type>    class TheClass    {        static TheClass *s_objectPtr;    };    template <typename Type>    TheClass<Type> *TheClass<Type>::s_objectPtr = 0;
As usual, the definition can be read from the variable name back to thebeginning of the definition:s_objectPtr of the classTheClass<Type>is a pointer to an object ofTheClass<Type>.

When a static variable of a template's type parameter's type is defined,it should of course not be given the initial value 0. The default constructor(e.g.,Type()) is usually more appropriate. Example:

    template <typename Type>                    // s_type's definition    Type TheClass<Type>::s_type = Type();

22.2.1: Extended use of the keyword `typename'

Until now the keywordtypename has been used to indicate a template typeparameter. However, it is also used to disambiguate code inside templates. Consider the following function template:
    template <typename Type>    Type function(Type t)    {        Type::Ambiguous *ptr;        return t + *ptr;    }

When this code is processed by the compiler, it complains with an -atfirst sight puzzling- error message like:

    4: error: 'ptr' was not declared in this scope

The error message is puzzling as it was the programmer's intention todeclare a pointer to a typeAmbiguous defined within the class templateType. But the compiler, confronted withType::Ambiguous may interpretthe statement in various ways. Clearly it cannot inspectType itselftrying to uncoverType's true nature asType is a templatetype. Because of thisType's actual definition isn't available yet.

The compiler is confronted with two possibilities: eitherType::Ambiguous is astatic member of the as yet mysterious templateType, or it is asubtype ofType. As the standardspecifies that the compiler must assume the former, the statement

    Type::Ambiguous *ptr;

is interpreted as amultiplication of the static memberType::Ambiguous and the (now undeclared) entityptr. The reason forthe error message should now be clear: in this contextptr is unknown.

To disambiguate code in which an identifier refers to a subtype of a template type parameter the keywordtypename must beused. Accordingly, the above code is altered into:

    template <typename Type>    Type function(Type t)    {        typename Type::Ambiguous *ptr;        return t + *ptr;    }

Classes fairly often define subtypes. When such subtypes appear insidetemplate definitions as subtypes of template type parameters thetypenamekeywordmust be used to identify them as subtypes. Example: a classtemplateHandler defines atypename Container as its template typeparameter. It also defines a data member storing the iterator returned by thecontainer'sbegin member. In additionHandler offers a constructoraccepting any container supporting abegin member.Handler's classinterface could then look like this:

    template <typename Container>    class Handler    {        Container::const_iterator d_it;        public:            Handler(Container const &container)            :                d_it(container.begin())            {}    };

What did we have in mind when designing this class?

The final consideration is an indication thattypename is required. Ifthis is omitted and aHandler is instantiated the compiler produces apeculiar compilation error:
    #include "handler.h"    #include <vector>    using namespace std;    int main()    {        vector<int> vi;        Handler<vector<int> > ph{ vi };    }    /*        Reported error:    handler.h:4: error: syntax error before `;' token    */

Clearly the line

    Container::const_iterator d_it;

in the classHandler causes a problem. It is interpreted by thecompiler as astatic member instead of a subtype. The problem issolved usingtypename:

    template <typename Container>    class Handler    {        typename Container::const_iterator d_it;        ...    };

An interesting illustration that the compiler indeed assumesX::a tobe a membera of the classX is provided by the error message we getwhen we try to compilemain using the following implementation ofHandler's constructor:

    Handler(Container const &container)    :        d_it(container.begin())    {        size_t x = Container::ios_end;    }    /*        Reported error:        error: `ios_end' is not a member of type `std::vector<int,                std::allocator<int> >'    */

Now consider what happens if the function template introduced at thebeginning of this section doesn't return aType value, but aType::Ambiguous value. Again, a subtype of a template type is referred to,andtypename must be used:

    template <typename Type>    typename Type::Ambiguous function(Type t)    {        return t.ambiguous();    }

Typenames can be embedded in using declarations. As is often the case,thisreduces the complexities of declarations and definitions appearingelsewhere. In the next example the typeIterator is defined as a subtypeof the template typeContainer.Iterator may now be used withoutrequiring the use of the keywordtypename:

    template <typename Container>    class Handler    {        using Iterator = Container::const_iterator;        Iterator d_it;        ...    };

22.3: Specializing class templates for deviating types

The classCirQue can be used for many different types. Their commoncharacteristic is that they can simply be pointed at by the class'sd_datamember. But this is not always as simple as it looks. What ifData turnsout to be avector<int>? For such data types the vanillaCirQueimplementation cannot be used and a specialization could be considered. Whenconsidering a specialization one should also consider inheritance. Often aclass derived from the class template accepting the incompatible datastructure as its argument but otherwise equal to the original class templatecan easily be designed. The developmental advantage of inheritance overspecialization is clear: the inherited class inherits the members of its baseclass while the specialization inherits nothing. All members defined by theoriginal class template must be implemented again by the class template'sspecialization.

The specialization considered here is a true specialization in that thedata members and representation used by the specialization greatly differ fromthe originalCirQue class template. Therefore all members defined by theorginal class template must be modified to fit the specialization's dataorganization.

Like function template specializations class template specializationsstart with a template header that may or may not have an empty templateparameter list. If the template parameters are directly specialized by thespecialization it remains empty (e.g.,CirQue's template type parameterData is specialized forchar * data). But the template parameter listmay showtypename Data when specializing for avector<Data>, i.e., avector storing any type of data. This leads to the following principle:

A template specialization is recognizedby the template argument list following a function or class template'sname andnot by an empty template parameter list. Class templatespecializations may have non-empty template parameter lists. If so, apartial class template specialization is defined.

A completely specialized class has the following characteristics:

22.3.1: Example of a class specialization

Here is an example of a completely specializedCirQue class, specializedfor avector<int>. All members of the specialized class are declared, butonly non-trivial implementations of its members are provided. The specializedclass uses a copy of thevector passed to the constructor and implements acircular queue using itsvector data member:

#ifndef INCLUDED_CIRQUEVECTOR_H_#define INCLUDED_CIRQUEVECTOR_H_#include <vector>#include "cirque.h"template<>class CirQue<std::vector<int>>{    using IntVect =  std::vector<int>;    IntVect d_data;    size_t d_size;    using iterator = IntVect::iterator;    iterator d_front;    iterator d_back;    public:        using value_type = int;        using const_reference = value_type const &;        enum Exception        {            EMPTY,            FULL        };        CirQue();        CirQue(IntVect const &iv);        CirQue(CirQue<IntVect> const &other);        CirQue &operator=(CirQue<IntVect> const &other);        int &back();        int &front();        bool empty() const;        bool full() const;        size_t maxSize() const;        size_t size() const;        void pop_front();        void push_back(int const &object);        void swap(CirQue<IntVect> &other);    private:        iterator inc(iterator const &iter);};CirQue<std::vector<int>>::CirQue():    d_size(0){}CirQue<std::vector<int>>::CirQue(IntVect const &iv):    d_data(iv),    d_size(iv.size()),    d_front(d_data.begin()),    d_back(d_data.begin()){}CirQue<std::vector<int>>::CirQue(CirQue<IntVect> const &other):    d_data(other.d_data),    d_size(other.d_size),    d_front(d_data.begin() + (other.d_front - other.d_data.begin())),    d_back(d_data.begin() + (other.d_back - other.d_data.begin())){}CirQue<std::vector<int>> &CirQue<std::vector<int>>::operator=(                                        CirQue<IntVect> const &rhs){    CirQue<IntVect> tmp(rhs);    swap(tmp);}void CirQue<std::vector<int>>::swap(CirQue<IntVect> &other){    char tmp[sizeof(CirQue<IntVect>)];    memcpy(tmp, &other, sizeof(CirQue<IntVect>));    memcpy(reinterpret_cast<char *>(&other), this, sizeof(CirQue<IntVect>));    memcpy(reinterpret_cast<char *>(this), tmp, sizeof(CirQue<IntVect>));}void CirQue<std::vector<int>>::pop_front(){    if (d_size == 0)        throw EMPTY;    d_front = inc(d_front);    --d_size;}void CirQue<std::vector<int>>::push_back(int const &object){    if (d_size == d_data.size())        throw FULL;    *d_back = object;    d_back = inc(d_back);    ++d_size;}inline int &CirQue<std::vector<int>>::back(){    return d_back == d_data.begin() ? d_data.back() : d_back[-1];}inline int &CirQue<std::vector<int>>::front(){    return *d_front;}CirQue<std::vector<int>>::iterator CirQue<std::vector<int>>::inc(    CirQue<std::vector<int>>::iterator const &iter){    iterator tmp(iter + 1);    tmp =  tmp == d_data.end() ? d_data.begin() : tmp;    return tmp;}#endif

The next example shows how to use the specializedCirQue class:

static int iv[] = {1, 2, 3, 4, 5};int main(){    vector<int> vi(iv, iv + 5);    CirQue<vector<int>> ci(vi);    cout << ci.size() << ' ' << ci.front() << ' ' << ci.back() << '\n';    ci.pop_front();    ci.pop_front();    CirQue<vector<int>> cp;    cp = ci;    cout << cp.size() << ' ' << cp.front() << ' ' << cp.back() << '\n';    cp.push_back(6);    cout << cp.size() << ' ' << cp.front() << ' ' << cp.back() << '\n';}/*    Displays:        5 1 5        3 3 5        4 3 6*/

22.4: Partial specializations

In the previous section class template specializations were introduced. Inthis section we'll introduce a variant of this specialization, both innumber and type of template parameters that are specialized.Partialspecializations may be defined for class templates having multiple template parameters.

Function templates cannot be partially specialized: there is no need for that, as a `partially specialized function template' merely is a functiontemplate that is tailored to particular types of some of its parameters. Sincefunction templates can be overloaded, `partially specializing' a functiontemplate simply means that overloads have to be defined for those specializedparameter types.

With partial specializations a subset (any subset) of template type parametersare given specific values. It is also possible to use a class template partialspecialization when the intent is to specialize the class template, but toparameterize the data type that is processed by the specialization.

To start our discussion with an example of the latter use of a partial classtemplate specialization consider the classCirQue<vector<int>>developed in the previous section. When designingCirQue<vector<int>>you may have asked yourself how many specializations you'd have toimplement. One forvector<int>, one forvector<string>, one forvector<double>? As long as the data types handled by thevector usedby the classCirQue<vector<...>> behaves like anint (i.e., is avalue-type of class) the answer is: zero. Instead of defining fullspecializations for each new data type the data type itself can beparameterized, resulting in a partial specialization:

    template <typename Data>    class CirQue<std::vector<Data>>    {        ...    };

The above class is a specialization as a template argument list isappended to theCirQue class name. But as the class template itself has anon-empty template parameter list it is in fact recognized as a partialspecialization. There is one characteristic that distinguishes theimplementation (subsequent to the class template's interface) of a classtemplate member function of a partial specialization from the implementationof a member function of a full specialization. Implementations of partiallyspecialized class template member functions receive a template header. Notemplate headers are used when implementing fully specialized class templatemembers.

Implementing the partial specialization forCirQue is not difficult and isleft as an exercise for the reader (hints: simply changeint intoDatain theCirQue<vector<int>> specialization of the previous section).Remember to prefix the typeiterator bytypename (as inusingiterator = DataVect::iterator) (as discussed in section22.2.1).

In the next subsections we'll concentrate on specializing class templatenon-type template parameters. These partial specializations are nowillustrated using some simple concepts defined in matrix algebra, a branch oflinear algebra.

22.4.1: Intermezzo: some simple matrix algebraic concepts

In this section some simple matrix algebraic terms are introduced. These termsare used in the next sections to illustrate and discuss partialspecializations of class templates. Readers proficient in matrix algebra mayskip this section without loss of continuity.

A matrix is commonly thought of as a table of some rows and columns,filled with numbers. Immediately we recognize an opening for using templates:the numbers might be plaindouble values, but they could also be complexnumbers, for which our complex container (cf. section12.4) mightprove useful. Our class template is therefore provided with aDataTypetemplate type parameter. It is specified when amatrix is constructed. Some simple matrices usingdouble values, are:

    1   0   0           An identity matrix,    0   1   0           (a 3 x 3 matrix).    0   0   1    1.2  0    0    0    A rectangular matrix,    0.5  3.5  18  23    (a 2 x 4 matrix).    1   2   4   8       A matrix of one row                        (a 1 x 4 matrix), also known as a                        `row vector' of 4 elements.                        (column vectors are analogously defined)

Various operations are defined on matrices. They may, for example beadded, subtracted or multiplied. Here we will not focus on theseoperations. Rather, we concentrate on some simple operations: computingmarginals and sums.

Marginals are the sums of row elements or the sums of column elements of amatrix. These two kinds of marginals are also known as, respectively,row marginals andcolumn marginals.

The following example shows a matrix, its marginals, and the sum of itsvalues:
    -------------------------------------                                row                matrix          marginals                ---------                1   2   3        6                4   5   6       15                ---------    column      5   7   9       21  (sum)    marginals    -------------------------------------

22.4.2: The Matrix class template

We'll start out by introducing a class template defining a matrix. Havingdefined this class template we'll continue with defining severalspecializations.

Since matrices consist of well defined numbers of rows and columns (thedimensions of the matrix), that normally do not change when matrices areused, we might consider specifying their values as template non-typeparameters. TheDataType = double will be used in the majority ofcases. Therefore,double can be selected as the template's default typeargument. Since it's a sensible default, theDataType template typeparameter is used last in the template type parameter list.

Our template classMatrix begins its life as:

    template <size_t Rows, size_t Columns, typename DataType = double>    class Matrix    ...

What do we want our class template to offer?

22.4.3: The MatrixRow partial specialization

Class templatepartial specializations can be defined for any (subset) of template parameters. They can be defined for template type parametersand for template non-type parameters alike. Our first partial specializationdefines a row of a genericMatrix, mainly (but not only) used for theconstruction of column marginals. Here is how such a partial specialization isdesigned: Now that we have defined the genericMatrix class and thepartial specialization defining a single row the compiler selects therow's specialization whenever aMatrix is defined usingRow = 1. Forexample:
    Matrix<4, 6> matrix;        // generic Matrix template is used    Matrix<1, 6> row;           // partial specialization is used

22.4.4: The MatrixColumn partial specialization

The partial specialization for aMatrixColumn is constructedsimilarly. Let's present its highlights (the fullMatrix class templatedefinition as well as all its specializations are provided in thecplusplus.yo.zip archive (that can be obtained from theC++ Annotations'Gitlab website) in thefileyo/classtemplates/examples/matrix.h):

22.4.5: The 1x1 matrix: avoid ambiguity

The reader might wonder what happens if we define the following matrix:
    Matrix<1, 1> cell;

Is this aMatrixRow or aMatrixColumn specialization? The answeris: neither.

It's ambiguous, precisely becauseboth the columnsand the rows couldbe used with a (different) template partial specialization. If such aMatrix is actually required, yet another specialized template must bedesigned.

Since this template specialization can be useful to obtain the sum of theelements of aMatrix, it's covered here as well.

Finally, themain function shown below illustrates how theMatrixclass template and its partial specializations can be used:

    #include <iostream>    #include "matrix.h"    using namespace std;    int main(int argc, char **argv)    {        Matrix<3, 2> matrix(cin);        Matrix<1, 2> colMargins(matrix);        cout << "Column marginals:\n";        cout << colMargins[0] << " " << colMargins[1] << '\n';        Matrix<3, 1> rowMargins(matrix);        cout << "Row marginals:\n";        for (size_t idx = 0; idx < 3; idx++)            cout << rowMargins[idx] << '\n';        cout << "Sum total: " << Matrix<1, 1>(matrix) << '\n';    }    /*        Generated output from input: 1 2 3 4 5 6        Column marginals:        9 12        Row marginals:        3        7        11        Sum total: 21    */

22.5: Variadic templates

Up to this point we've encountered templates defining a fixed number oftemplate parameters. However, templates may also defined asvariadic templates, allowing any number ofarguments to be passed to their instantiations.

Variadic templates are defined for function templates and for classtemplates. Variadic templates allow us to specify an arbitrary number oftemplate arguments of any type.

Variadic templates were added to the language to prevent us from having todefine many overloaded templates and to be able to createtype safevariadic functions.

AlthoughC (andC++) supportvariadic functions, their use hasalways been deprecated inC++ as those functions are notoriouslytype-unsafe. Variadic function templates can be used to process objectsthat until now couldn't be processed properly byC-style variadicfunctions.

Template headers of variadic templates use the phrasetypename ...Params (Params being a formalname). A variadic class templateVariadic could be declared as follows:

    template<typename ...Params> class Variadic;

Assuming the class template's definition is available then this templatecan be instantiated using any number of template arguments. Example:

    class Variadic<            int,            std::vector<int>,            std::map<std::string, std::vector<int>>    > v1;

The template argument list of a variadic template can also beempty. Example:

    class Variadic<> empty;

If this is considered undesirable using an empty template argument listcan be prevented by providing one or more fixed parameters. Example:

    template<typename First, typename ...Rest>    class tuple;

C's functionprintf is a well-known example of a type-unsafefunction. It is turned into a type-safe function when it is implemented as avariadic function template. Not only does this turn the function into atype-safe function but it is also automatically extended to accept any typethat can be defined byC++. Here is a possible declaration of a variadicfunction templateprintcpp:

    template<typename ...Params>    void printcpp(std::string const &strFormat, Params ...parameters);

Theellipsis (...) used in the declaration serves two purposes:

C++ offers no syntax to access the individual template argumentsdirectly. However, the arguments can be visited recursively. An example isprovided in the next section. Thenumber of arguments is determined using anew invocation of thesizeof operator:
    template<typename ...Params>    struct StructName    {        enum: size_t { s_size = sizeof ...(Params) };    };    // StructName<int, char>::s_size          -- initialized to 2

22.5.1: Defining and using variadic templates

The arguments associated with a variadic template parameter are not directlyavailable to the implementation of a function or class template. We have toresort to other means to obtain them.

By defining a partial specialization of a variadic template, explicitlydefining an additional template type parameter, we can associate the firsttemplate argument of a parameter pack with this additional (first) typeparameter. The setup of such a variadic function template (e.g.,printcpp,see the previous section) is as follows:

The overloaded non-template function prints the remainder of the formatstring,en passant checking for any left-over format specifications:

    void printcpp(string const &format)    {        size_t left = 0;        size_t right = 0;        while (true)        {            if ((right = format.find('%', right)) == string::npos)                break;            if (format.find("%%", right) != right)                throw std::runtime_error(                            "printcpp: missing arguments");            ++right;            cout << format.substr(left, right - left);            left = ++right;        }        cout << format.substr(left);    }

Here is the variadic function template's implementation:

    template<typename First, typename ...Params>    void printcpp(std::string const &format, First value, Params ...params)    {        size_t left = 0;        size_t right = 0;        while (true)        {            if ((right = format.find('%', right)) == string::npos)      // 1                throw std::runtime_error("printcpp: too many arguments");            if (format.find("%%", right) != right)                      // 2                break;            ++right;            cout << format.substr(left, right - left);            left = ++right;        }        cout << format.substr(left, right - left) << value;        printcpp(format.substr(right + 1), params...);   }

Make sure that the overloaded function is at least declared before thecompiler processes the function template's definition or it won'tcall the overloaded functionprintcpp when compiling the functiontemplate.

Different fromC'sprintf functionprintcpp only recognizes% and%% as format specifiers. The above implementation does notrecognize, e.g., field widths. Type specifiers like%c and%x are ofcourse not needed asostream's insertion operator is aware of the types ofthe arguments that are inserted into theostream. Extending the formatspecifiers so that field widths etc. are recognized by thisprintcppimplementation is left as an exercise to the reader. Here is an exampleshowing howprintcpp can be called:

    printcpp("Hello % with %%main%% called with % args"                                            " and a string showing %\n",        "world", argc, "A String"s);

22.5.2: Perfect forwarding

Considerstring's memberinsert.String::insert has severaloverloaded implementations. It can be used to insert text (completely orpartially) provided by astring or by achar const * argument; toinsert single characters a specified number of times; iterators can be used tospecify the range of characters to be inserted; etc., etc.. All in all,string offers as many as five overloadedinsert members.

Assume the existence of a classInserter that is used to insertinformation into all kinds of objects. Such a class could have astringdata member into which information can be inserted.Inserter's interfaceonly partially has to copystring's interface to realize this: onlystring::insert's interfaces must be duplicated. The members duplicatinginterfaces often contain one statement (calling the appropriate memberfunction of the object's data member) and are for this reason oftenimplemented in-line. Suchwrapper functions merelyforward theirparameters to the matching member functions of the object's data member.

Another example is found infactory functions that also frequently forwardtheir parameters to the constructors of objects that they return.

C++ simplifies and generalizes forwarding of parameters byofferingperfect forwarding, implemented through rvalue references andvariadic templates. With perfect forwarding the arguments passed to functionsare `perfectly forwarded' to nested functions. Forwarding is calledperfect as the arguments are forwarded in a type-safe way. To use perfectforwarding nested functions must define parameter lists matching theforwarding parameters both in types and number.

Perfect forwarding is easily implemented:

In the next example perfect forwarding is used to implementone memberInserter::insert that can be used to call any of the five overloadedstring::insert members. Theinsert function that's actually called nowsimply depends on the types and number of arguments that are passed toInserter::insert:

    class Inserter    {        std::string d_str;  // somehow initialized        public:                            // constructors not implemented,                            // but see below            Inserter();            Inserter(std::string const &str);            Inserter(Inserter const &other);            Inserter(Inserter &&other);            template<typename ...Params>            void insert(Params &&...params)            {                d_str.insert(std::forward<Params>(params)...);            }    };

A factory function returning anInserter can also easily be implementedusing perfect forwarding. Rather than defining four overloaded factoryfunctions a single one now suffices. By providing the factory function with anadditional template type parameter specifying the class of the object toconstruct the factory function is turned into a completely general factoryfunction:

    template <typename Class, typename ...Params>    Class factory(Params &&...params)    {        return Class(std::forward<Params>(params)...);    }

Here are some examples showing its use:

    Inserter inserter(factory<Inserter>("hello"));    string delimiter(factory<string>(10, '='));    Inserter copy(factory<Inserter>(inserter));

The functionstd::forward is provided by the standard library. Itperforms no magic, but merely returnsparams as a nameless object. Thatway it acts likestd::move that also removes the name from an object,returning it as a nameless object. The unpack operator has nothing to do withthe use offorward but merely tells the compiler to applyforward toeach of the arguments in turn. Thus it behaves similarly toC's ellipsisoperator used by variadic functions.

Perfect forwarding was introduced in section21.4.5: a templatefunction defining aType &&param, withType being a template typeparameter convertsType && toTp & if the function is called withan argument of typeTp &. Otherwise it bindsType toTp,withparam being defined asTp &&param. As a result anlvalueargument binds to an lvalue-type (Tp &), while anrvalue argumentbinds to an rvalue-type (Tp &&).

The functionstd::forward merely passes the argument (and its type) on tothe called function or object. Here is its simplified implementation:

    typedef <type T>    T &&forward(T &&a)    {        return a;    }

SinceT && turns into an lvalue reference whenforward is calledwith an lvalue (or lvalue reference) and remains an rvalue reference ifforward is called with an rvalue reference, and sinceforward (likestd::move) anonymizes the variable passed as argument toforward, theargument value is forwarded while passing its type from the function'sparameter to the called function's argument.

This is calledperfect forwarding as the nested function can only becalled if the types of the arguments that were used when calling the `outer'function (e.g.,factory) exactly match the types of the parameters of thenested function (e.g.,Class's constructor). Perfect forwarding thereforeis a tool to realize type safety.

A cosmetic improvement toforward requires users offorward tospecify the type to use rather than to have the compiler deduce the type as aresult of the function template parameter type deduction's process. This isrealized by a small support struct template:

    template <typename T>    struct identity    {        using type = T;    };

This struct merely definesidentity::type asT, but as it is astruct it must be specified explicitly. It cannot be determined from thefunction's argument itself. The subtle modification to the aboveimplementation offorward thus becomes (cf. section22.2.1 foran explanation of the use oftypename):

    typedef <type T>    T &&forward(typename identity<T>::type &&arg)    {        return arg;    }

Nowforward must explicitly statearg's type, as in:

    std::forward<Params>(params)

Using thestd::forward function and the rvalue reference specificationis not restricted to the context of parameter packs. Because of the specialway rvalue references to template type parameters are treated (cf. section21.4.5) they can profitably be used to forward individual function parameters as well. Here is an example showing how an argument to a functioncan be forwarded from a template to a function that is itself passed to thetemplate as a pointer to an (unspecified) function:

    template<typename Fun, typename ArgType>    void caller(Fun fun, ArgType &&arg)    {        fun(std::forward<ArgType>(arg));    }

A functiondisplay(ostream &out) andincrement(int &x) may nowboth be called throughcaller. Example:

    caller(display, cout);    int x = 0;    caller(increment, x);

22.5.3: The unpack operator

Theunpack operator is used to obtain template arguments in manysituations. No mechanism other than recursion (as shown in section22.5.1) is available to obtain the individual types and values of avariadic template.

The unpack operator can also be used to define template classes that arederived from any number of base classes. Here is how it's done:

    template <typename ...BaseClasses>    class Combi: public BaseClasses...          // derive from base classes    {        public:                                                // specify base class objects                                                // to its constructor using                                                // perfect forwarding            Combi(BaseClasses &&...baseClasses)            :                BaseClasses(baseClasses)...     // use base class initializers            {}                                  // for each of the base    };                                          // classes

This allows us to define classes that combine the features of any numberof other classes. If the classCombi is derived of classesA, B, andC thenCombi is-anA, B, andC. It should of course be given avirtual destructor. ACombi object can be passed to functions expectingpointers or references to any of its base class type objects. Here is anexample definingCombi as a class derived from a vector of complexnumbers, a string and a pair of ints and doubles (using uniform intializers ina sequence matching the sequence of the types specified for the usedCombitype):

    using MultiTypes = Combi<        vector<complex<double>>, string, pair<int, double> >;    MultiTypes mt = { {3.5, 4}, "mt", {1950, 1.72} };

The same construction can also be used to define template data memberssupporting variadic type lists such astuples (cf. section22.6). Such a class could be designed along these lines:

    template <typename ...Types>    struct Multi    {        std::tuple<Types...> d_tup;        // define tuple for Types types        Multi(Types ...types)        :                                   // initialize d_tup from Multi's            d_tup(std::forward<Types>(types)...)   //             arguments        {}    };

The ellipses that are used when forwarding parameter packs areessential. The compiler considers their omission an error. In the followingstruct definition it was the intent of the programmer to pass a parameterpack on to a nested object construction but ellipses were omitted whilespecifying the template parameters, resulting in aparameter packs not expanded with `...' error message:

    template <int size, typename ...List>    struct Call    {        Call(List &&...list)        {            Call<size - 1, List &&> call(std::forward<List>(list)...);        }    };

Instead of the above definition of thecall object the programmershould have used:

    Call<size - 1, List &&...> call(std::forward<List>(list)...);

22.5.4: Non-type variadic templates

Variadic templates not necessarily define template types. Non-types can alsobe used with variadic templates. The following function template accepts any seriesofint values, forwarding these values to a class template. The classtemplate defines anenum valueresult which is returned by thefunction, unless no int values were specified, in which case 0 is returned.
    template <int ...Ints>    int forwarder()    {        return computer<Ints ...>::result;  // forwarding the Ints    }    template <>     // specialization if no ints are provided    int forwarder<>()    {        return 0;    }    // use as:    cout << forwarder<1, 2, 3>() << ' ' << forwarder<>() << '\n';

Thesizeof operator can be used for variadic non-type parameters aswell:sizeof...(Ints) would return 3 when used in the first functiontemplate forforwarder<1, 2, 3>().

Variadic non-type parameters are used to define variadic literaloperators, introduced in section23.3.

22.5.5: Folding expressions

Functions accepting variable numbers of arguments (of possibly varying types)are commonly handled using variadic templates. Implementations usuallyprocess the first argument, passing the remaining arguments to an overloadedfunction, which is defined by the compiler for these remaining argumenttypes. One of the overloaded versions, accepting zero or one argument, endsthe compiler's recursive implementations.

Sometimes the arguments must be combined using binary operators (likearg1+ arg2 + ...). In those cases afolding expression can be used insteadof combining the arguments using a traditional variadic template.

All binary operators (including the assignment, compound assignment and commaoperators) can be used in folding expressions.

There are no special restrictions that apply to function templates usingfold expressions. Directly returning the result of the fold expression is OK,but the result could also be used in another expression (maybe inserting itsvalue into anostream), or when initializing a variable or object. Also,the types of the arguments do not have to be identical: the only requirementis that the fully expanded expression (in the example:1 + 2 + 3 is avalid expression).

Together, unary left folds and unary right folds are calledunary folds.

If a binary operator has been overloaded, it will be used where applicable. Awell-known example is of courseoperator<< as defined forstd::ostreamobjects. A binary fold defined foroperator<< will not only do shifts, butcan also be used to insert a series of arguments intocout:

    template <class ...Pack>    ostream &out2(ostream &out, Pack &&...args)    {        return (out << ... << args);    };

Here is another interesting example: a fold expression for the comma operator:

    template <class ...Pack>    void call(Pack &&...args)    {        ... , args();    };

This unary fold calls each of its arguments in sequence. Arguments couldbe function addresses, function object or lambda functions. Note the use ofthe rvalue reference when defining the variadic parameter list which preventscopying of function objects that might be passed tocall. Assuming thatstruct Functor defines a functor, and thatvoid function() has beendefined, thencall could be called this way:

    Functor functor;    call(functor, function, functor,         []()        {            // ...            }    );

Finally: don't forget the parentheses surrounding fold expressions: they are required!

22.6: Tuples

C++ offers ageneralized pair container: thetuple, covered inthis section. Before tuples can be used the header file<tuple> must beincluded.

Whereasstd::pair containers have limited functionality and only supporttwo members, tuples have slightly more functionality and may contain anunlimited number of different data types. In that respect a tuple can beconsidered the `template's answer toC's struct'.

A tuple's generic declaration (and definition) uses the variadic templatenotation:

    template <class ...Types>    class tuple;

Here is an example of its use:

    using tuple_idsc = std::tuple<int, double &, std::string, char const *>;    double pi = 3.14;    tuple_idsc idsc(59, pi, "hello", "fixed");    // access a field:    std::get<2>(idsc) = "hello world";

Thestd::get<idx>(tupleObject) function template returns areference to theidxth (zero based) field of the tupletupleObject. The index is specified as the function template's non-typetemplate argument.

Type-based tuple addressing () can beused for tuple types used once in a tuple definition (if the same type is usedrepeatedly referring to that type introduces an ambiguity). The next exampleshows how to refer to the elements in the above example by type:

    get<int>(idsc)              // 59    get<double &>(idsc)         // 3.14    get<string>(idsc)           // "hello"s    get<char const *>(idsc)     // "fixed"

Tuples may be constructed without specifying initial values. Primitivetypes are initialized to zeroes; class type fields are initialized by theirdefault constructors. Be aware that in some situations the construction of atuple may succeed but its use may fail. Consider:

    tuple<int &> empty;    cout << get<0>(empty);

Here the tupleempty cannot be used as itsint & field is anundefined reference. However,empty's construction succeeds.

Tuples may be assigned to each other if their type lists are identical; ifsupported by their constituent types copy constructors are available aswell. Copy construction and assignment is also available if a right-hand typecan be converted to its matching left-hand type or if the left-hand type canbe constructed from the matching right-hand type. Tuples(matching in number and (convertible) types) can be compared using relationaloperators as long as their constituent types support comparisons. In thisrespect tuples are like pairs.

Tuples offer the following static elements (using compile-timeinitialization):

The unpack operator can also be used to forward the arguments of aconstructor to a tuple data member. Consider a classWrapper that isdefined as a variadic template:

    template <typename ...Params>    class Wrapper    {        ...        public:            Wrapper(Params &&...params);    };

This class may be given a tuple data member which should be initialized bythe types and values that are used when initializing an object of the classWrapper using perfectforwarding. Comparable to the way a class may inherit from its template types(cf. section22.5.3) it may forward its types and constructor argumentsto its tuple data member:

    template <typename ...Params>    class Wrapper    {        std::tuple<Params...> d_tuple;     // same types as used for                                            // Wrapper itself        public:            Wrapper(Params &&...params)            :                               // initialize d_tuple with                                            // Wrapper's arguments                d_tuple(std::forward<Params>(params)...)            {}    };

22.6.1: Tuples and structured bindings

Structured bindings were introduced in section3.3.7.1. Thatsection concentrated on associating structured binding declarations withdata members of structs, returned as POD values.

Structured bindings, however, can also be used in a much more generic way, byassociating them with tuples. By doing so structured bindings don't have to beassociated with data members, but they may hold values returned by classmembers.

The association between structured binding declarations and tuples is verystrong. In fact it is so strong that the standard explicitly allows users todefine tuple specializations, even though tuple specializations live in thestandard namespace, which otherwise is off-limits to programmers (except, ofcourse, when using its features).

In order to allow structured bindings to be associated with class membersthe following steps must be performed:

The flexibility that is achieved by being able to use structured bindings thisway is enormous. As long as a class offers members returning values thosemembers can be associated with structured binding variables. The memberfunctions don't even have to return values that are immediately available(e.g, as data members accessors) but their return values could also becomputed on the spot, by simply referring to the corresponding structuredbinding variable.

To illustrate the abovementioned steps for associating structured bindingswith class members we use these classes:

    class Euclid    {        size_t d_x;        size_t d_y;            public:            Euclid(size_t x = 0, size_t y = 0);            double distance() const;        // sqrt(d_x * d_x + d_y * d_y)    };    class Data    {        std::string d_text{ "hello from Data" };        Euclid d_euclid;            public:            void setXY(size_t x, size_t y);                Euclid factory() const;            double distance() const;            std::string const &text() const;    };

The first step towards allowing structured bindings forData consists ofdefining a (member) termplateget. IfData is our own class we canadd a member templateget. Alternatively, if we're only interested inaccessingData's public members we could derive a classDataGet fromData, and provide that class with theget member template. Yet anotherpossibility is to define a freeget function template. Thegetfunction must return whatever we're interested in. To make the appropriateselection we use an integral type (int, size_t, ...) selector value, andthe function template thus only has a non-type template parameter. Rather thandefining several specializations using theif constexpr clause is advised,as it greatly simplifies the function's definition.

Ourget function defines selector 0 forfactory, selector 1 fordistance and selector 2 fortext. Thedistance member simplyreturnsd_euclid.distance(), andEuclid::distance is run-timeevaluated using itsd_x andd_y values. Thus,distance is anexample of a function that will be run-time evaluated when we're referring,later on, to the third structured bindings variable.

Here's the definition of the member templateget:

    template <size_t Nr>    auto get() const    {        if constexpr (Nr == 0)            return factory();            if constexpr (Nr == 1)            return distance();            if constexpr (Nr == 2)            return text();            static_assert(Nr >= 0 and Nr < 3);    }

This function is still suboptimal. Consider its specialization for value 2: itreturnsData::text(). Asauto merely inspects the returned data type,get<2>() returns astd::string, rather than (text's) return type,which is astd::string const &. To use the return types that are actuallyreturned byData's member functions,get's return type should bedefined asdecltype(auto) rather than justauto:

    template <size_t Nr>    decltype(auto) get() const    ...

When definingget as a free function template it must be provided with aparameterData const &data (orData &data if members are used that maymodifydata's data members), returning the parameter's memberfunctions. E.g.,

        // get as a free function template:    template <size_t Nr>    decltype(auto) get(Data const &data)    {        if constexpr (Nr == 0)            return data.factory();            if constexpr (Nr == 1)            return data.distance();            ... // (etc)    }

Now that step 1 has been completed, we turn our attention to thestd::tuple specializations. These specializations are defined inside thestd namespace(usingnamespace std { ... } orstd::tuple...).

The specialization ofstd::tuple_size forData definesstatic size_t const value as the number of index valuesthat can be specified with theget<idx> function:

    template<>    struct std::tuple_size<Data>    {        static size_t const value = 3;    };

The specialization ofstd::tuple_element forData returns the typesmatching the various return types of theget member template. Itsimplementation also provides a nice example wheredeclval is used: thereturn type of theget<Nr> specialization must be determined. But toobtain that return type aData object is required, which isn't availableby just mentioningData intupe_element's specialization. However,declval<Data>() defines an rvalue reference, whichcan be passed toget<Nr>. But the function's returnvalue isn't required, so the objectdoesn't have to be constructed. Only its return type is needed. Hence, bysurrounding theget<Nr> call bydecltype no object is constructed, andmerely its return type is used:

    template<size_t Nr>    struct std::tuple_element<Nr, Data>    {        using type = decltype( declval<Data>().get<Nr>() );        // if get<Nr> is a free function use:        //           decltype( get<Nr>( declval<Data>() ) );        };

Astuple_size andtuple_element are directly associated with the classData their definitions should be provided inData's header file, belowData's class interface.

Here's how this could be used in amain function, showing single objectaccess and access using a range-based for-loop:

    int main()    {        Data data;        auto &[ ef, dist, txt ] = data;        // or maybe:        // auto &&[ ef, dist, txt ] = Data{};            cout << dist << ' ' << txt << '\n';            Data array[5];        for (auto &[ ef, dist, txt]: array)            cout << "for: " << dist << ' ' << txt << '\n';    }

22.7: Computing the return type of function objects

As amply illustrated in chapter19 function objects play animportant role with generic algorithms. Like generic algorithms themselves,function objects can be generically defined as members of class templates. Ifthe function call operators (operator()) of such classes define parametersthen the types of those parameters may also be abstracted by defining thefunction call operators themselves as member templates. Example:
    template <typename Class>    class Filter    {        Class obj;        public:            template <typename Param>            Param operator()(Param const &param) const            {                return obj(param);            }    };

The template classFilter is a wrapper aroundClass, filteringClass's function call operator through its own function call operator. Inthe above example the return value ofClass's function call operator issimply passed on, but any other manipulation is of course also possible.

A type that is specified asFilter's template type argument may ofcourse have multiple function call operators:

    struct Math    {        int operator()(int x);        double operator()(double x);    };

Math objects can now be filtered usingFilter<Math> fm usingMath's first or second function call operator, depending on the actualargument type. Withfm(5) theint-version is used, withfm(12.5)thedouble-version is used.

However, this scheme doesn't work if the function call operators havedifferent return and argument types. Because of this the following classConvert cannot be used withFilter:

    struct Convert    {        double operator()(int x);           // int-to-double        std::string operator()(double x);   // double-to-string    };

This problem can be tackled successfully by the class templatestd::result_of<Functor(Typelist)>. Before usingstd::result_of theheader file<functional> must be included.

Theresult_of class template offers a using declaration (type),representing the type that is returned byFunctor<TypeList>. It can beused as follows to improve the implementation ofFilter:

    template <typename Class>    class Filter    {        Class obj;        public:            template <typename Arg>                typename std::result_of<Class(Arg)>::type                operator()(Arg const &arg) const                {                    return obj(arg);                }    };

Using this definition,Filter<Convert> fc can be constructed. Nowfc(5) returns adouble, whilefc(4.5) returns astd::string.

The classConvert must define the relationships between its function calloperators and their return types. Predefined function objects (like those inthe standard template library) already do so, but self-defined functionobjects must do this explicitly.

If a function object class defines only one function call operator it canspecify its return type through a using-declaration. If the above classConvert would only define the first of its two function call operatorsthen the using declaration (in the class'spublic section) should be:

    using type = double;

If multiple function call operators are defined, each with its own signatureand return type, then the association between signature and return type is setup as follows (all in the class'spublic section):

22.8: Instantiating class templates

Class templates are instantiated when anobject of a class template is defined. When a class template object isdefined or declared its template parameters must explicitly be specified.

Template parameters arealso specified when default template parametervalues are specified albeit that in that case the compiler provides thedefaults (cf. section22.4 wheredouble is used as the defaulttype to use for the template'sDataType parameter). The actual values ortypes of template parameters arenever deduced from arguments as is done with function templateparameters. So to define aMatrix of complex-valued elements, thefollowing syntax is used:

    Matrix<3, 5, std::complex> complexMatrix;

Since the class templateMatrix uses a default data typea matrix ofdouble-valued elements can be defined like this:

    Matrix<3, 5> doubleMatrix;

A class template object may bedeclared using the keywordextern. For example, todeclare the matrixcomplexMatrix use:

    extern Matrix<3, 5, std::complex> complexMatrix;

A class template declaration suffices to compile return values orparameters that are of class template types. Example: the following sourcefile may be compiled, although the compiler hasn't seen the definition of theMatrix class template. Generic classes and (partial) specializations mayall be declared. A function expecting or returning a class template object,reference, or parameter automatically becomes a function template itself. Thisis necessary to allow the compiler to tailor the function to the types ofvarious actual arguments that may be passed to the function:

    #include <cstddef>    template <size_t Rows, size_t Columns, typename DataType = double>    class Matrix;    template <size_t Columns, typename DataType>    class Matrix<1, Columns, DataType>;    Matrix<1, 12> *function(Matrix<2, 18, size_t> &mat);

When class templates areused the compiler must first have seen theirimplementations. So, template member functions must be known to the compilerwhen the template is instantiated. This does not mean thatall members ofa template class are instantiated or must have been seen when a class templateobject is defined. The compiler only instantiates those members that are actually used. Thisis illustrated by the following simple classDemo that has twoconstructors and two members. When we use one constructor and call one memberinmain note the sizes of the resulting object file and executableprogram. Next the class definition is modified in that the unused constructorand member are commented out. Again we compile and link the program. Nowobserve that these latter sizes are identical to the former sizes. There areother ways to illustrate that only used members are instantiated. Thenmprogram could be used. It shows the symbolic content of object files. Usingnm we'll reach the same conclusion:only template member functions thatare actually used are instantiated. Here is the class templateDemo thatwas used for our little experiment. Inmain only the first constructorand the first member function are called and thus only these members wereinstantiated:

    #include <iostream>    template <typename Type>    class Demo    {        Type d_data;        public:            Demo();            Demo(Type const &value);            void member1();            void member2(Type const &value);    };    template <typename Type>    Demo<Type>::Demo()    :        d_data(Type())    {}    template <typename Type>    void Demo<Type>::member1()    {        d_data += d_data;    }    // the following members should be commented out before    // compiling for the 2nd time:    template <typename Type>    Demo<Type>::Demo(Type const &value)    :        d_data(value)    {}    template <typename Type>    void Demo<Type>::member2(Type const &value)    {        d_data += value;    }    int main()    {        Demo<int> demo;        demo.member1();    }

22.9: Processing class templates and instantiations

In section21.13 the distinction between code depending on templateparameters and code not depending on template parameters was introduced.The same distinction also holds true when class templates are defined andused.

Code not depending on template parameters is verified by the compiler whenthe template is defined. If a member function in a class template uses aqsort function, thenqsort does not depend on a templateparameter. Consequently,qsort must be known to the compiler when itencountersqsort's function call. In practice this implies that the<cstdlib> header file must have been read by the compiler before itis able to compile the class template definition.

On the other hand, if a template defines a<typename Ret> templatetype parameter to parameterize the return type of some template memberfunction as in:

    Ret member();

then the compiler may encountermember or the class to whichmember belongs in the following locations:

22.10: Declaring friends

Friend functions are normally constructed assupport (free)functions of a class that cannot be implemented and declared as classmembers. The insertion operator for output streams is a well-knownexample. Friend classes are most often seen in the context of nested classes.Here the inner class declares the outer class as its friend (or the other wayaround). Again we see a support mechanism: the inner class is constructed tosupport the outer class.

Like ordinary classes, class templates may declare other functions andclasses as their friends. Conversely, ordinary classes may declare templateclasses as their friends. Here too, the friend is constructed as a specialfunction or class augmenting or supporting the functionality of the declaringclass. Although thefriend keyword can be used by any type of class (ordinary or template) when using class templates the following casesshould be distinguished:

22.10.1: Non-templates used as friends in templates

A class template may declare an ordinary function, ordinary memberfunction or ordinary class as its friend. Such afriend may access theclass template'sprivate members.

Concrete classes and ordinary functions can be declared as friends, butbefore a single member function of a class can be declared as a friend, thecompiler must have seen theclass interface declaring that member. Let'sconsider the various possibilities:

22.10.2: Templates instantiated for specific types as friends

Withbound friend class or function templates thereis a one-to-one mapping between the template arguments of the friend templatesand the template arguments of the class templates declaring them asfriends. In this case, the friends themselves are templates too. Here are thevarious possibilities:

Finally, the following example can be used as a prototype forsituations where bound friends are useful:

    template <typename T>   // a function template    void fun(T *t)    {        t->not_public();    };    template<typename X>    // a free member function template    bool operator==(A<X> const &lhs, A<X> const &rhs);    template <typename X>   // a class template    class A    {                       // fun() is used as friend bound to A,                            // instantiated for X, whatever X may be        friend void fun<A<X>>(A<X> *);                            // operator== is a friend for A<X> only        friend  bool operator==<>(A<X> const &lhs, A<X> const &rhs);        int d_data = 10;        public:            A();        private:            void not_public();    };    template <typename X>    A<X>::A()    {        fun(this);    }    template <typename X>    void A<X>::not_public()    {}    template<typename X>    // may access lhs/rhs's private data    bool operator==(A<X> const &lhs, A<X> const &rhs)    {        return lhs.d_data == rhs.d_data;    }    int main()    {        A<int> a;        fun(&a);            // fun instantiated for A<int>.    }

22.10.2.1: Free operators as friends of nested classes

When nesting a class under a class template it may be desirable to providethat nested class with free operators, which are bound to the surroundingclass. This situation is encountered when defining a nested iterator for whicha freeoperator==(iterator const &lhs, iterator const &rhs) operator ispreferred over a memberoperator==(iterator const &rhs) const, as themember does not allow promotions of the lhs operand.

It is possible to define the freeoperator== as friend in the nestedclass, which then automatically becomes a bound friend. E.g.,

    #include <string>    template <typename Type>    struct String    {        struct iterator        {            std::string::iterator d_iter;            friend bool operator==(iterator const &lhs, iterator const &rhs)            {                return lhs.d_iter == rhs.d_iter;            }        };        iterator begin()        {            return iterator{};        }    };    int main()    {        String<int> str;        return  str.begin() == str.begin();    }
However, this requires an in-class implementation, which should be avoidedas it combines interfaces and implementations which reduces clarity.

But when moving the implementation out of the interface we run into theproblem thatoperator== is a function template, which must be declared assuch in the class interface. The compiler suggests tomake sure thefunction template has already been declared and add '<>' after the functionname. But declaring

    template <typename Type>    bool operator==(Type const &lhs, Type const &rhs);
before thestruct String class template, and then implementingoperator== as
    template <typename Type>    inline bool operator==(String<Type>::iterator const &lhs,                           String<Type>::iterator const &rhs)    {        return lhs.d_iter == rhs.d_iter;    }
the compiler still complains, reportingdeclaration of 'operator==' asnon-function.

So how to solve this issue? There are two known ways to solve thisproblem. One was suggested by Radu Cosma (teaching assistant of ourC++course in the 2021-2022 academic year), the other solution is provided in section23.13.7.2.

Radu proposed using an application ofSFINAE: by defining a free operatorprovided with a type uniquely defined by the nested class, which is thenprovided with a default value in the free function's implementation, thecompiler automatically selects the appropriate overloaded function. This worksfor multiple classes declaring nested classes and for classes definingmultiple nested classes alike. Here is an example of two classes eachdeclaring a nested classiterator:

    template <typename Type>    struct String    {        struct iterator        {            using StringType_iterator = int;            friend bool operator==<>(iterator const &lhs, iterator const &rhs);                std::string::iterator d_iter;        };        iterator begin()        {            return iterator{};        }    };        template <typename Type>    struct Container    {        struct iterator        {            using ContainerType_iterator = int;            friend bool operator==<>(iterator const &lhs, iterator const &rhs);                int *d_ptr;        };        iterator begin()        {            return iterator{};        }    };
Note that the nested classes declare the free functions as functiontemplate specializations.

Then, for each nested class an implementation of the free operator isprovided. These implementations are function templates provided with a template typeType and a second type which is the uniquelynamedint type of the nested class for which the free operator isimplemented:

    template <typename Type, Type::StringType_iterator = 0>    inline bool operator==(Type const &lhs, Type const &rhs)    {        return lhs.d_iter == rhs.d_iter;    }        template <typename Type, Type::ContainerType_iterator = 0>    inline bool operator==(Type const &lhs, Type const &rhs)    {        return lhs.d_ptr == rhs.d_ptr;    }
Themain function illustrates the use of both free operators:
    int main()    {        String<int> str;        Container<int> cnt;        return str.begin() == str.begin() and cnt.begin() == cnt.begin();    }

22.10.3: Unbound templates as friends

When a friend is declared as anunbound friend it merely declares anexisting template to be its friend (no matter how it is instantiated). Thismay be useful in situations where the friend should be able to instantiateobjects of class templates declaring the friend, allowing the friend to accessthe instantiated object's private members. Functions, classes, and memberfunctions may all be declared as unbound friends.

Here are the syntactic conventions declaring unbound friends:

22.10.4: Extended friend declarations

Throughextended frienddeclarations, which are also available forclass templates, template type parameters can be declared as friends. Atemplate type argument, however, does not necessarily have to be a type forwhich the keywordfriend makes sense, likeint. In those cases thefriend declaration is simply ignored.

Consider the following class template, declaringFriend as a friend:

    template <typename Friend>    class Class    {        friend Friend;        void msg();             // private, displays some message    };

Now, an actualFriend class may access all ofClass's members

    class Concrete    {        Class<Concrete> d_class;        Class<std::string> d_string;       public:            void msg()            {                d_class.msg();    // OK: calls private Class<Concrete>::msg()                //d_string.msg(); // fails to compile: msg() is private            }    };

A declaration likeClass<int> intClass is also OK, but here the frienddeclaration is simply ignored. After all, there are no `int members' to accessClass<int>'s private members.

22.11: Class template derivation

Class templates can be used for inheritance purposes as well. When aclass template is used in class derivation, the following situations should bedistinguished: These three variants of class templatederivation are elaborated in this and the upcoming sections.

Consider the following base class:

    template<typename T>    class Base    {        T const &t;        public:            Base(T const &t);    };

The above class is a class template that can be used as a base class forthe following derived class templateDerived:

    template<typename T>    class Derived: public Base<T>    {        public:            Derived(T const &t);    };    template<typename T>    Derived<T>::Derived(T const &t)    :        Base(t)    {}

Other combinations are also possible. The base class may be instantiatedby specifying template arguments, turning the derived class into an ordinaryclass (showing a class object's definition as well):

    class Ordinary: public Base<int>    {        public:            Ordinary(int x);    };    inline Ordinary::Ordinary(int x)    :        Base(x)    {}    Ordinary ordinary{ 5 };

This approach allows us to add functionality to a class template, without the need for constructing aderived class template.

Class template derivation pretty much follows the same rules as ordinaryclass derivation, not involving class templates. Some subtleties that arespecific for class template derivation may easily cause confusion like the useofthis when members of a template base class are called from a derivedclass. The reasons for usingthis are discussed in section23.1. Inthe upcoming sections the focus will be on the class derivation proper.

22.11.1: Deriving ordinary classes from class templates

When an existing class template is used as a base class for deriving anordinary class, the class template parameters are specified when defining thederived class's interface. If in a certain context an existing class templatelacks a particular functionality, then it may be useful to derive an ordinaryclass from a class template. For example, although the classmap caneasily be used in combination with thefind_if() generic algorithm(section19.1.15), it requires the construction of a class and at leasttwo additional function objects of that class. If this is considered too muchoverhead then extending a class template with tailor-made functionalitymight be considered.

Example: a program executing commands entered at the keyboard might accept allunique initial abbreviations of the commands it defines. E.g., the commandlist might be entered asl, li, lis orlist. By deriving a classHandler from

    map<string, void (Handler::*)(string const &cmd)>

and by defining a member functionprocess(string const &cmd) to do theactual command processing a program might simply execute the followingmain() function:

    int main()    {        string line;        Handler cmd;        while (getline(cin, line))            cmd.process(line);    }
The classHandler itself is derived from astd::map, in whichthe map's values are pointers toHandler's member functions, expecting thecommand line entered by the user. Here areHandler's characteristics:

22.11.2: Deriving class templates from class templates

Although it's perfectly acceptable to derive an ordinary class from aclass template, the resulting class of course has limited generality comparedto its template base class. If generality is important, it's probably a betteridea to derive a class template from a class template. This allows us toextend an existing class template with new functionality or to override thefunctionality of the existing class template.

The class templateSortVector presented below is derived from theexisting class templatestd::vector. It allows us to perform ahierarchic sort of its elements using any ordering of any data membersits data elements may contain. To accomplish this there is but onerequirement.SortVector's data type must offer dedicated memberfunctions comparing its members.

For example, ifSortVector's data type is an object of classMultiData, thenMultiData should implement member functions having thefollowing prototypes for each of its data members which can be compared:

    bool (MultiData::*)(MultiData const &rhv)

So, ifMultiData has two data members,int d_value andstd::string d_text and both may be used by a hierarchic sort, thenMultiData should offer the following two members:

    bool intCmp(MultiData const &rhv);  // returns d_value < rhv.d_value    bool textCmp(MultiData const &rhv); // returns d_text  < rhv.d_text

Furthermore, as a convenience it is assumed thatoperator<< andoperator>> have been defined forMultiData objects.

The class templateSortVector is directly derived from the templateclassstd::vector. Our implementation inherits all members from that baseclass. It also offers two simple constructors:

    template <typename Type>    class SortVector: public std::vector<Type>    {        public:            SortVector()            {}            SortVector(Type const *begin, Type const *end)            :                std::vector<Type>(begin, end)            {}

Its memberhierarchicSort is the trueraison d'être for theclass. It defines the hierarchic sort criteria. It expects a pointer to a series of pointers to member functions of the classType as well as asize_t representing the size of the array.

The array's first element indicatesType's most significant sortcriterion, the array's last element indicates the class's least significantsort criterion. Since thestable_sort generic algorithm was designedexplicitly to support hierarchic sorting, the member uses this genericalgorithm to sortSortVector's elements. With hierarchic sorting, theleast significant criterion should be sorted first.hierarchicSort'simplementation is therefore easy. It requires a support classSortWithwhose objects are initialized by the addresses of the member functions passedto thehierarchicSort() member:

    template <typename Type>    void SortVector<Type>::hierarchicSort(                bool (Type::**arr)(Type const &rhv) const,                size_t n)    {        while (n--)            stable_sort(this->begin(), this->end(), SortWith<Type>(arr[n]));    }

The classSortWith is a simple wrapper class around a pointer toa predicate function. Since it depends onSortVector's actual datatype the classSortWith must also be a class template:

    template <typename Type>    class SortWith    {       bool (Type::*d_ptr)(Type const &rhv) const;

SortWith's constructor receives a pointer to a predicate function andinitializes the class'sd_ptr data member:

    template <typename Type>    SortWith<Type>::SortWith(bool (Type::*ptr)(Type const &rhv) const)    :        d_ptr(ptr)    {}

Its binary predicate member (operator()) must returntrue if itsfirst argument should eventually be placed ahead of its second argument:

    template <typename Type>    bool SortWith<Type>::operator()(Type const &lhv, Type const &rhv) const    {        return (lhv.*d_ptr)(rhv);    }

The following examples, which can be embedded in amainfunction, provides an illustration:

After compiling the program the following command can be given:
    echo a 1 b 2 a 2 b 1 | a.out

This results in the following output:

    a 1 b 2 a 2 b 1    ====    a 1 a 2 b 1 b 2    ====    a 1 b 1 a 2 b 2    ====

22.11.3: Deriving class templates from ordinary classes

An ordinary class may be used as the base class for deriving a template class. The advantageof such an inheritance tree is that the base class's members may all becompiled beforehand. When objects of the class template are now instantiatedonly the actually used members of the derived class template must beinstantiated.

This approach may be used for all class templates having member functionswhose implementations do not depend on template parameters. These members maybe defined in a separate class which is then used as a base class of theclass template derived from it.

As an illustration of this approach we'll develop such a class templatebelow. We'll develop a classTable derived from an ordinary classTableType. The classTable displays elements of some type in atable having a configurable number of columns. The elements are eitherdisplayed horizontally (the firstk elements occupy the first row) orvertically (the firstr elements occupy a first column).

When displaying the table's elements they are inserted into a stream. Thetable is handled by a separate class (TableType), implementing the table'spresentation. Since the table's elements are inserted into a stream, theconversion to text (orstring) is implemented inTable, but thehandling of the strings themselves is left toTableType. We'll cover somecharacteristics ofTableType shortly, concentrating onTable'sinterface first:

This completes the implementation of the classTable. Note that thisclass template only has three members, two of them beingconstructors. Therefore, in most cases only two function templates must beinstantiated: a constructor and the class'sfill member. For example, thefollowing defines a table having four columns, vertically filled bystrings extracted from the standard input stream:

    Table<istream_iterator<string> >        table(istream_iterator<string>(cin), istream_iterator<string>(),              4, TableType::VERTICAL);

The fill-direction is specified asTableType::VERTICAL. It could also have been specified usingTable,but sinceTable is a class template its specification would have beenslightly more complex:Table<istream_iterator<string> >::VERTICAL.

Now that theTable derived class has been designed, let's turn ourattention to the classTableType. Here are its essential characteristics:

The support classTableSupport is used to display headers, footers,captions and separators. It has four virtual members to perform those tasks(and, of course, a virtual constructor):

The reader is referrred to thecplusplus.yo.zip archive for the fullimplementation of the classesTable,TableType andTableSupport.Here is a little program showing their use:
    /*                                  table.cc    */    #include <fstream>    #include <iostream>    #include <string>    #include <iterator>    #include <sstream>    #include "tablesupport/tablesupport.h"    #include "table/table.h"    using namespace std;    using namespace FBB;    int main(int argc, char **argv)    {        size_t nCols = 5;        if (argc > 1)        {            istringstream iss(argv[1]);            iss >> nCols;        }        istream_iterator<string>   iter(cin);   // first iterator isn't const        Table<istream_iterator<string> >            table(iter, istream_iterator<string>(), nCols,                  argc == 2 ? TableType::VERTICAL : TableType::HORIZONTAL);        cout << table << '\n';    }    /*        Example of generated output:        After: echo a b c d e f g h i j | demo 3            a e i            b f j            c g            d h        After: echo a b c d e f g h i j | demo 3 h            a b c            d e f            g h i            j    */

22.12: Static Polymorphism

Chapter14 introduced polymorphism. Polymorphism allows us touse a base class's interface to call implementations which are defined inderived classes. Traditionally this involves definingVtables forpolymorphic classes, containing pointers to functions that can be overriddenin derived classes. Objects of polymorphic classes harbor hidden pointers,pointing to their class's Vtables. This type of polymorphism is calleddynamic polymorphism, and it useslate binding as the function tocall is determined run-time, rather than compile-time.

In many cases, however, dynamic polymorphism isn't really required. Usuallythe derived class objects that are passed to functions expecting base classreferences are invariants: at fixed locations in programs fixed class typesare used to create objects. The polymorphic nature of these objects is usedinside the functions that receive these objects, expecting references to theirbase classes. As an example consider reading information from a networksocket. A classSocketBuffer is derived fromstd::streambuf, and thestd::stream receiving a pointer to theSocketBuffer merely usesstd::streambuf's interface. However, the implementation, by usingpolymorphism, in fact communicates with functions defined inSocketBuffer.

The disadvantages of this scheme are that, firstly, inside the functionsexpecting references to polymorphic base classes execution is somewhat sloweddown precisely because of late-binding. Member functions aren'tdirectly called, but are called indirectly via the object'svpointer andtheir derived class'sVtable. Secondly, programs using dynamicpolymorphism tend to become somewhat bloated compared to programs using staticbinding. Thecode bloat is caused by the requirement to satisfy atlink-time all the references that are mentioned in all the object filescomprising the final program. This requirement forces the linker to link allthe functions whose addresses are stored in theVtables of all polymorphicclasses, even if these functions are never actually called.

Static polymorphism allows us to avoid thesedisadvantages. It can be used instead of dynamic polymorphism in cases wherethe abovementioned invariant holds. Static polymorphism, also called thecuriously recurring template pattern (CRTP), is an example oftemplate meta programming (see also chapter23 foradditional examples of template meta programming).

Whereas dynamic polymorphism is based on the concepts ofvpointers,Vtables, andfunction overriding, static polymorphism capitalizes on thefact that function templates (c.q., member templates) are only compiled intoexecutable code when they are actually called. This allows us to write code inwhich functions are called which are completely non-existent at the time wewrite our code. This, however, shouldn't worry us too much. After all, we usea comparable approach when calling a purely virtual function of an abstractbase class. The function is really called, but which function is eventuallycalled is determined later in time. With dynamic polymorphism it is determinedrun-time, with static polymorphism it is determined compile-time.

There's no need to consider static and dynamic polymorphism as mutuallyexclusive variants of polymorphism. Rather, both can be used together,combining their strengths.

This section contains several subsections.

22.12.1: An example of static polymorphism

With static polymorphism a class template takes the role of a base class indynamic polymorphism. This class template declares several members, which arecomparable to members of a polymorphic base class: they are either supportmembers or they call members that are overridden in derived classes.

In the context of dynamic polymorphism these overridable members are the baseclass's virtual members. In the context of static polymorphism there are novirtual members. Instead, the statically polymorphic base class (referred toas the `base class' below) declares atemplate type parameter (referred toas the `derived class type' below). Next, the base class's interfacing memberscall members of the derived class type.

Here is a simple example: a class template acting as a base class. Its publicinterface consists of one member. But different from dynamic polymorphismthere's no reference in the class's interface to any member showingpolymorphic behavior (i.e, no `virtual' members are declared):

    template <class Derived>    struct Base    {        void interface();    }

Let's have a closer look at the member `interface'. This member is calledby functions receiving a reference or pointer to the base class, but it maycall members that must be available in the derived class type at the pointwhereinterface is called. Before we can call members of the derived classtype an object of the derived class type must be available. This object isobtained through inheritance. The derived class type is going to be derivedfrom the base class. ThusBase's this pointer is alsoDerived's thispointer.

Forget about polymorphism for a second: when we have aclass Derived:public Base then (because of inheritance) astatic_cast<Derived *> can beused to cast aBase * to aDerived object. Adynamic_cast ofcourse doesn't apply, as we don't use dynamic polymorphism. But astatic_cast is appropriate since ourBase *does in fact point toaDerived class object.

So, to call aDerived class member from insideinterface wecan use the following implementation (remember thatBase is a base classofDerived):

    template<class Derived>    void Base<Derived>::interface()    {        static_cast<Derived *>(this)->polymorphic();    }

It's remarkable that, when the compiler is given this implementation itcannot determine whetherDerived isreally derived fromBase. Neither can it determine whether the classDerived indeed offersa memberpolymorphic. The compiler simplyassumes this to be true. Ifso, then the provided implementation is syntactically correct. One of thekey characteristics of using templates is that the implementation's viabilityis eventually determined at the function's point of instantiation (cf. section21.6). At that point the compiler will verify that, e.g., thefunctionpolymorphic really is available.

Thus, in order to use the above scheme we must ensure that

The first requirement is satisfied by using thecuriously recurring template pattern:
    class First: public Base<First>

In this curious pattern the classFirst is derived fromBase,which itself is instantiated forFirst. This is acceptable, as thecompiler already has determined that the typeFirst exists. At this pointthat is all it needs.

The second requirement is simply satisfied by defining the memberpolymorphic. In chapter14 we saw that virtual andoverriding members belong to the class's private interface. We can apply thesame philosophy here, by placingpolymorphic inFirst's privateinterface, allowing it to be accessed from the base class by declaring

    friend void Base<First>::interface();

First's complete class interface can now be designed, followed bypolymorphic's implementation:

    class First: public Base<First>    {        friend void Base<First>::interface();        private:            void polymorphic();    };    void First::polymorphic()    {        std::cout << "polymorphic from First\n";    }

Note that the classFirst itself is not a class template: its memberscan be separately compiled and stored in, e.g., a library. Also, as is thecase with dynamic polymorphism, the memberpolymorphic has full access toall ofFirst's data members and member functions.

Multiple classes can now be designed likeFirst, each offering theirown implementation ofpolymorphic. E.g., the memberSecond::polymorphic of the classSecond, designed likeFirst,could be implemented like this:

    void Second::polymorphic()    {        std::cout << "polymorphic from Second\n";    }

The polymorphic nature ofBase becomes apparent once a functiontemplate is defined in whichBase::interface is called. Again, thecompiler simply assumes a memberinterface exists when it reads thedefinition of the following function template:

    template <class Class>    void fun(Class &object)    {        object.interface();    }

Only where this function is actually called will the compiler verify theviability of the generated code. In the followingmain function aFirst object is passed tofun:First declaresinterfacethrough its base class, andFirst::polymorphic is called byinterface. The compiler will at this point (i.e., wherefun is called)check whetherfirst indeed has a memberpolymorphic. Next aSecondobject is passed tofun, and here again the compiler checks whetherSecond has a memberSecond::polymorphic:

    int main()    {        First first;        fun(first);        Second second;        fun(second);    }

There are also downsides to using static polymorphism:

Summarizing, static polymorphism is best used in situations where a smallnumber of different derived class types are used, where a fixed number ofderived class objects are used, and where the statically polymorphic baseclasses themselves are lean (possibly encapsulating some of their code inordinary base classes of their own).

22.12.2: Converting dynamic polymorphic classes to static polymorphic classes

So you've decided that you want to convert some of your dynamicallypolymorphic classes into statically polymorphic classes. Now what?

In chapter14 the base classVehicle and some derivedclasses were introduced.Vehicle, Car andTruck's interfaces look likethis (regarding the members that are involved in their polymorphic behaviors):

    class Vehicle    {        public:            int mass() const;        private:            virtual int vmass() const;    };    class Car: public Vehicle    {        private:            int vmass() const override;    };            class Truck: public Car    {        private:            int vmass() const override;    };

When converting dynamically polymorphic classes to statically polymorphicclasses we must realize that polymorphic classes show two importantcharacteristics:

With statically polymorphic classes these two characteristics shouldcompletely be separated. As we've seen in the previous section, astatically polymorphic derived class derives from its base class by using itsown class type as argument to the base class's type parameter. This works fineif there's only one level of inheritance (i.e., one base class, and one ormore classes that are directly derived from that base class).

With multiple levels of inheritance (e.g.,Truck -> Car -> Vehicle)Truck's inheritance specification becomes a problem. Here's an intialattempt to use static polymorphism and multiple levels of inheritance:

    template <class Derived>    class Vehicle    {        public:            void mass()             {                static_cast<Derived *>(this)->vmass();            }    };        class Car: public Vehicle<Car>    {        friend void Vehicle<Car>::mass();        void vmass();    };    class Truck: public Car    {        void vmass();    };

To solve this problem (i.e., to ensure thatTruck{}.mass() callsTruck::vmass) the redefinable interface must be separated from the inheritable interface.

In derived classes the protected and public interfaces of (direct or indirect)base classes are made available using standard inheritance. This is shown inthe left-hand side of figure29.

Figure 29: Vehicle Static Polymorphic Class Design

The left-hand side classes are used as base classes for the next level ofinheritance (except forTruckBase, butTruckBase could be used as baseclass for yet another level of class inheritance). This line of inheritancedeclares the inheritable interface of the classes.

Each of the classes to the left is a base class of a single class to theright:VehicleBase is a base class forVehicle,TruckBase forTruck. The classes to the left contain all members that are completelyindependent of the elements that are involved in realizing the staticpolymorphism. As that's a mere design principle to realize multiple levels ofstatic polymorphism the common data hiding principles are relaxed, and theleft-hand side classes declare their matching right-hand side derived classtemplates as friend, to allow full access to all members of a left-hand sideclass, including the private members, from the matching derived class templateto the right. E.g.,VehicleBase declaresVechicle as its friend:

    class VehicleBase    {        template <class Derived>        friend class Vehicle;        // all members originally in Vehicle, but not involved         // in realizing static polymorphism are declared here. E.g.,        size_t d_massFactor = 1;    };

The top level class to the left (VehicleBase) lays the foundation ofstatic polymorphism, by defining that part of the interface that uses thestatically redefinable functions. E.g, using the curiously recurring templatepattern it defines a class membermass that calls the functionvmassof its derived class (in addition it can use all members of its non-classtemplate base class). E.g,:

    template <class Derived>    class Vehicle: public VehicleBase    {        public:            int mass() const            {                return d_massFactor *                        static_cast<Derived const *>(this)->vmass();            }    };

Which function actually is called whenvmass is called depends on theimplementation in the classDerived, which is handled by the remainingclasses, shown belowVehicle, which are all derived fromVehicle (aswell as from their own...Base class). These classes use the curiouslyrecurring template pattern. E.g.,

    class Car: public CarBase, public Vehicle<Car>

So, ifCar now implements its ownvmass function, which can useany of its own (i.e.,CarBase's members), thenthat function is calledwhen calling aVehicle's mass function. E.g.,

    template <class Vehicle>    void fun(Vehicle &vehicle)    {        cout << vehicle.mass() << '\n';    }        int main()    {        Car car;        fun(car);       // Car's vmass is called            Truck truck;        fun(truck);     // Truck's vmass is called    }

Now that we've analyzed the design of statically polymorphic classes usingmultiple levels of inheritance let's summarize the steps we took toimplement static polymorphism

This design pattern can be extended to any level of inheritance: for eachnew level a base class is constructed, deriving from the most deeply nestedXXXBase class so far, and deriving fromVehicle<XXX>, implementing itsown ideas about the redefinable interface.

Functions that are used in combination with statically polymorphic classesthemselves must be function templates. E.g.,

    template <class Vehicle>    void fun(Vehicle &vehicle)    {        cout << vehicle.mass() << '\n';    }

Here,Vehicle is just a formal name. When an object is passed tofun it must offer a membermass, or compilation will fail. If theobject is in fact aCar orTruck, then theirVehicle<Type> staticbase class membermass is called, which in turn uses static polymorphismto call the membervmass as implemented by the actually passed classtype. The followingmain function displays, respectively, 1000 and 15000:

    int main()    {        Car car;        fun(car);            Truck truck;        fun(truck);    }

Note that this program implementsfun twice, rather than once in thecase of dynamic polymorphism. The same holds true for theVehicle classtemplate: two implementations, one for theCar type, and one for theTruck type. The statically polymorphic program will be slightly faster,though.

(A compilable example using static polymorphism is found in theC++ Annotations's source distribution's fileyo/classtemplates/examples/staticpolymorphism/polymorph.cc.)

22.12.3: Using static polymorphism to avoid reimplementations

Static polymorphism may profitably be used to avoid reimplementing code in anotherwise dynamic polymorphic environment.

Consider the situation where we have a class containing a container ofpointers to some polymorphic base class type (like the classVehicle fromchapter14). How to copy such a container to another container?We're not hinting at using shared pointers here, but would like to make a fullcopy. Clearly, we'll need to duplicate the objects the pointers point at, andassign these new pointers to the copied object's container.

The prototype design patttern is commonly used to create copies of objects ofpolymorphic classes, given pointers to their base classes.

To apply the prototype design pattern we have to implementnewCopy in allderived classes. Not by itself a big deal, but static polymorphism can nicelybe used here to avoid having to reimplement this function for each derivedclass.

We start off with an abstract base classVehicleBase declaring a purevirtual membernewCopy:

    struct VehicleBase    {        virtual ~VehicleBase();        VehicleBase *clone() const; // calls newCopy        // declare any additional members defining the         // public user interface here        private:            VehicleBase *newCopy() const = 0;       };

Next we define a static polymorphic classCloningVehicle which is derivedfromVehicleBase (note that we thus combine dynamic and staticpolymorphism). This class provides the generic implementation ofnewCopy. This is possible because all derived classes can use thisimplementation. Also,CloningVehicle will be re-implemented for eachconcrete type of vehicle that is eventually used: aCar, aTruck, anAmphibiousVehicle, etc, etc.CloningVehicle thus isn't shared (likeVehicleBase), but instantiated for each new type of vehicle.

The core characteristic of a statically polymorphic class is that it can useits class template type parameter via astatic_cast of its own type. Amember function likenewCopy is implemented always the same way, viz.,by using the derived class's copy constructor. Here is the classCloningVehicle:

    template <class Derived>    class CloningVehicle: public VehicleBase    {        VehicleBase *newCopy() const        {            return new Derived(*static_cast<Derived const *>(this));        }    };

And we're done. All types of vehicles should now be derived fromCloningVehicle, which automatically provides them with their ownimplementation ofnewCopy. E.g., a classCar looks like this:

    class Car: public CloningVehicle<Car>    {        // Car's interface, no need to either        // declare or implement newCopy,        // but a copy constructor is required.    };

Having defined astd::vector<VehicleBase *> original we could create acopy oforiginal like this:

    for(auto pointer: original)        duplicate.push_back(pointer->clone());

Irrespective of the actual type of vehicle to which the pointers point,theirclone members will return pointers to newly allocated copies ofobjects of their own types.

22.13: Class templates and nesting

When a class is nested within a class template, it automatically becomes aclass template itself. The nested class may use the template parameters of the surroundingclass, as shown by the following skeleton program. Within a classPtrVector, a classiterator is defined. The nested class receives itsinformation from its surrounding class, aPtrVector<Type> class. Sincethis surrounding class should be the only class constructing its iterators,iterator's constructor is madeprivate and the surrounding class isgiven access to the private members ofiterator using abound friend declaration. Here is the initial section ofPtrVector's class interface:
    template <typename Type>    class PtrVector: public std::vector<Type *>
This shows that thestd::vector base class stores pointers toTypevalues, rather than the values themselves. Of course, a destructor is nowrequired as the (externally allocated) memory for theType objects musteventually be freed. Alternatively, the allocation might be part ofPtrVector's tasks, when it stores new elements. Here it is assumed thatPtrVector's clients do the required allocations and that the destructor isimplemented later on.

The nested class defines its constructor as a private member, and allowsPtrVector<Type> objects access to its private members. Therefore onlyobjects of the surroundingPtrVector<Type> class type are allowed toconstruct theiriterator objects. However,PtrVector<Type>'s clientsmay constructcopies of thePtrVector<Type>::iterator objects theyuse.

Here is the nested classiterator, using a (required)frienddeclaration. Note the use of thetypename keyword: sincestd::vector<Type *>::iterator depends on a template parameter, it is notyet an instantiated class. Thereforeiterator becomes an implicittypename. The compiler issues a warning iftypename has beenomitted. Here is the class interface:

            class iterator            {                friend class PtrVector<Type>;                typename std::vector<Type *>::iterator d_begin;                iterator(PtrVector<Type> &vector);                public:                    Type &operator*();            };
The implementation of the members shows that the base class'sbeginmember is called to initialized_begin.PtrVector<Type>::begin'sreturn type must again be preceded bytypename:
    template <typename Type>    PtrVector<Type>::iterator::iterator(PtrVector<Type> &vector)    :        d_begin(vector.std::vector<Type *>::begin())    {}    template <typename Type>    Type &PtrVector<Type>::iterator::operator*()    {        return **d_begin;    }
The remainder of the class is simple. Omitting all other functions thatmight be implemented, the functionbegin returns a newly constructedPtrVector<Type>::iterator object. It may call the constructor since theclassiterator declared its surrounding class as its friend:
    template <typename Type>    typename PtrVector<Type>::iterator PtrVector<Type>::begin()    {        return iterator(*this);    }
Here is a simple skeleton program, showing how the nested classiterator might be used:
    int main()    {        PtrVector<int> vi;        vi.push_back(new int(1234));        PtrVector<int>::iterator begin = vi.begin();        std::cout << *begin << '\n';    }

Nested enumerations and nested typedefs and using declarations can also be definedby class templates. The classTable, mentioned before (section22.11.3) inherited the enumerationTableType::FillDirection. HadTable been implemented as a full class template, then this enumerationwould have been defined inTable itself as:

    template <typename Iterator>    class Table: public TableType    {        public:            enum FillDirection            {                HORIZONTAL,                VERTICAL            };        ...    };

In this case, the actual value of the template type parameter must bespecified when referring to aFillDirection value or to its type. Forexample (assumingiter andnCols are defined as in section22.11.3):

    Table<istream_iterator<string> >::FillDirection direction =                argc == 2 ?                    Table<istream_iterator<string> >::VERTICAL                :                    Table<istream_iterator<string> >::HORIZONTAL;    Table<istream_iterator<string> >        table(iter, istream_iterator<string>(), nCols, direction);

22.14: Constructing iterators

Section18.2 iterators as used by generic algorithms wereintroduced. We've seen that several types of iterators were distinguished:InputIterators, ForwardIterators, OutputIterators, BidirectionalIterators,RandomAccessIterators, and ContiguousIterators.

To ensure that an object of a class is interpreted as a particular type ofiterator, the class must be designed so that it satisfies the requirements ofthat type of iterator. The interface of a class implementing an iterator usesiterator_tags, which are available when including the<iterator> header file.

Iterator classes should provide the followingusing declarations:

Theseusing declarations are commonly encountered in the publicsections of iterators, which therefore might be defined as structs. Here's anexample of how an iterator's interface could be designed:

    struct Iterator    {        using iterator_category = std::random_access_iterator_tag;        using difference_type   = std::ptrdiff_t;        using value_type        = std::string;        using pointer           = value_type *;        using reference         = value_type &;         friend bool operator==(iterator const &lhs, iterator const &rhs);        friend auto operator<=>(Iterator const &lhs, Iterator const &rhs);        friend int operator-(Iterator const &lhs, Iterator const &rhs);        friend Iterator operator+(Iterator const &lhs, int step);        friend Iterator operator-(Iterator const &lhs, int step);     private:        // private data members     public:        // public members    private:        // private support members    }
In section18.2 the characteristics of iterators werediscussed. All iterators should support (usingIterator as the generic name of the designediterator class andType to represent the (possiblyconst, in whichcase the associated operator should be aconst member as well) data type towhichIterator objects refer):

When iterators are used in the context of generic algorithms they must meetadditional requirements. These are required because generic algorithms performchecks on the types of the iterators they receive. Simple pointers are usuallyaccepted, but if an iterator-object is used the generic algorithm mightrequire the iterator to specify what type of iterator it represents.

The type of iterator that is implemented is indicated by the class'siterator_category. The six standard iterator categories are:

Each iterator tag assumes that a certain set of operators isavailable. TheRandomAccessIterators antContiguousIterators are themost complex, as other iterators only offer subsets of their facilities.

Note that iterators are always defined over a certain range([begin, end)). Increment and decrement operations may result inundefined behavior of the iterator if the resulting iterator value would referto a location outside of this range.

Normally iterators of classes merely accesses the data elements of thoseclasses. Internally, an iterator might use an ordinary pointer but it ishardly ever necessary for the iterator to allocate memory. Therefore,iterators normally don't need to define copy constructors and assignmentoperators. For the same reason iterators usually don't require destructors.

Most classes offering members returning iterators do so by having membersconstruct the required iterators that are thereupon returned as objects. Asthecaller of these member functions only has touse or sometimescopy the returned iterator objects, there is usually no need to providepublicly available constructors, except for the copy constructor (which isavailable by default). Therefore additional constructors are usually defined asprivate orprotected members. To allow an outer class to createiterator objects, the iterator class usually declares the outer class as itsfriend.

In the following sections aRandomAccessIterator, and areverseRandomAccessIterator is covered. The container class for which a randomaccess iterator must be developed may actually store its data elements in manydifferent ways (e.g., using containers or pointers to pointers). Section26.5 contains an example of a self-made template iterator class.

The random access iterator developed in the next sections reaches dataelements that are only accessible through pointers. The iterator class isdesigned as an inner class of a class derived from a vector of stringpointers.

22.14.1: Implementing a `RandomAccessIterator'

In the chapter about containers (chapter12) it was noted thatcontainers own the information they contain. If they contain objects, thenthose objects are destroyed once the containers are destroyed. As pointers arenot objects their use in containers is discouraged (STL'sunique_ptr andshared_ptr type objects may be used, though). Although discouraged, wemight be able to use pointer data types in specific contexts. In the followingclassStringPtr, an ordinary class is derived from thestd::vectorcontainer that usesstd::string * as its data type:
    #ifndef INCLUDED_STRINGPTR_H_    #define INCLUDED_STRINGPTR_H_    #include <string>    #include <vector>    class StringPtr: public std::vector<std::string *>    {        public:            StringPtr(StringPtr const &other);            ~StringPtr();            StringPtr &operator=(StringPtr const &other);    };    #endif
This class needs a destructor: as the object stores string pointers, adestructor is required to destroy the strings once theStringPtr objectitself is destroyed. Similarly, a copy constructor and an overloadedassignment operator are required. Other members (in particular: constructors)are not explicitly declared here as they are not relevant to this section'stopic.

Assume that we want to use thesort generic algorithm withStringPtr objects. This algorithm (see section19.1.54) requires twoRandomAccessIterators. Although these iterators are available (viastd::vector'sbegin andend members), they return iterators tostd::string *s. When comparingstring * values the pointer valuesinstead of the content of the strings are compared, which is not what we want.

To remedy this, an internal typeStringPtr::iterator is defined,not returning iterators to pointers, but iterators to theobjects thesepointers point to. Once thisiterator type is available, we can add thefollowing members to ourStringPtr class interface, hiding the identicallynamed, but useless members of its base class:

    StringPtr::iterator begin();    // returns iterator to the first element    StringPtr::iterator end();      // returns iterator beyond the last                                    // element

Since these two members return the (proper) iterators, the elements in aStringPtr object can easily be sorted:

    int main()    {        StringPtr sp;               // assume sp is somehow filled        sort(sp.begin(), sp.end()); // sp is now sorted    }

To make this work, a typeStringPtr::iterator is defined. As suggested byits type name,iterator is a nested type ofStringPtr. To use aStringPtr::iterator in combination with thesort generic algorithm itmust be aRandomAccessIterator, whosevalue_type is astd::string. Therefore, the iterator specifies:

    using iterator_category = std::random_access_iterator_tag;    using difference_type   = std::ptrdiff_t;    using value_type        = std::string;    using pointer           = value_type *;    using reference         = value_type &;

Now we're ready to redesignStringPtr's class interface. It offersmembers returning (reverse) iterators, and a nestediterator class. Hereis its interface:

struct StringPtr: public std::vector<std::string *>{    class iterator;    using reverse_iterator = std::reverse_iterator<iterator>;    iterator begin();    iterator end();    reverse_iterator rbegin();    reverse_iterator rend();};struct StringPtr::iterator{//USING    using iterator_category = std::random_access_iterator_tag;    using difference_type   = std::ptrdiff_t;    using value_type        = std::string;    using pointer           = value_type *;    using reference         = value_type &;

Next we have a look atStringPtr::iterator's characteristics:

The interfaces required for other iterator types are simpler, requiringonly a subset of the interface required by a random access iterator. E.g.,the forward iterator is never decremented and never incremented over arbitrarystep sizes. Consequently, in that case all decrement operators andoperator+(int step) can be omitted from the interface. Of course, the tagto use would then bestd::forward_iterator_tag. The tags (and the set ofrequired operators) vary accordingly for the other iterator types.

22.14.2: Implementing a `reverse_iterator'

Once we've implemented an iterator, the matchingreverse iterator canbe implemented in a jiffy. To implement a reverse_iteratorstd::reverse_iterator is used, which nicelyimplements the reverse iterator once we have defined an iterator. Itsconstructor merely requires an object of the iterator type for which we wantto construct a reverse iterator.

To implement a reverse iterator forStringPtr we only need to definethereverse_iterator type in its interface. This requires us to specifyonly one line of code, which must be inserted after the interface of the classiterator:

    using reverse_iterator = std::reverse_iterator<iterator>;

Also, the well-known membersrbegin andrend are added toStringPtr's interface. Again, they can easily be implemented inline:

inline StringPtr::reverse_iterator StringPtr::rbegin(){    return reverse_iterator(end());}inline StringPtr::reverse_iterator StringPtr::rend(){    return reverse_iterator(begin());}
Note the arguments thereverse_iterator constructors receive: thebegin point of the reversed iterator is obtained by providingreverse_iterator's constructor with the value returned by the memberend: theendpoint of the normal iterator range; theendpoint ofthe reversed iterator is obtained by providingreverse_iterator'sconstructor with the value returned by the memberbegin: thebeginpoint of the normal iterator range.

The following program illustrates the use ofStringPtr'sRandomAccessIterator:

    #include <iostream>    #include <algorithm>    #include "stringptr.h"    using namespace std;    int main(int argc, char **argv)    {        StringPtr sp;        while (*argv)            sp.push_back(new string{ *argv++ });        sort(sp.begin(), sp.end());        copy(sp.begin(), sp.end(), ostream_iterator<string>(cout, " "));        cout << "\n======\n";        sort(sp.rbegin(), sp.rend());        copy(sp.begin(), sp.end(), ostream_iterator<string>(cout, " "));        cout << '\n';    }    /*            when called as:        a.out bravo mike charlie zulu quebec            generated output:        a.out bravo charlie mike quebec zulu        ======        zulu quebec mike charlie bravo a.out    */
Although it is thus possible to construct a reverse iterator from a normaliterator, the opposite does not hold true: it is not possible toinitialize a normal iterator from a reverse iterator.

Assume we would like to process all lines stored invector<string>lines up to any trailing empty lines (or lines only containing blanks) itmight contain. How should we proceed? One approach is to start the processingfrom the first line in the vector, continuing until the first of the trailingempty lines. However, once we encounter an empty line it does of course nothave to be the first line of the set of trailing empty lines. In that case,we'd better use the following algorithm:

However, we can't mix iterators and reverse iterators when using genericalgorithms. So how can we initialize the second iterator using the availablereverse_iterator? The solution is not very difficult, as an iterator maybe initialized from a pointer. Although the reverse iteratorrit is not apointer,&*(rit - 1) or&*--ritis. So we use
    for_each(lines.begin(), &*--rit, Process());
to process all the lines up to the first of the set of trailing emptylines. In general, ifrit is areverse_iterator pointing to someelement and we need aniterator to point to that element, we may use&*rit to initialize the iterator. Here, the dereference operator isapplied to reach the element the reverse iterator refers to. Then the addressoperator is applied to obtain its address with which we can initialize theiterator.

When defining aconst_reverse_iterator (e.g., matching aconst_iterator class), then theconst_iterator's operator* membershould be a member returning a non-modifiable value or object. Since aconst_reverse_iterator uses the iterator'soperator-- member,we're running against a small conceptual conflict. On the one hand, astd::input_iterator_tag is inappropriate, since we must allow decrementingthe iterator. On the other hand, astd::bidirectional_iterator isinappropriate, since we don't allow modification of the data.

Iterator tags are primarily conceptual. Ifconst_iterators andconst_reverse_iterators only allow increment operations, then aninput_iterator_tag is the best match for the iterator's intendeduse. Hence this tag is used below.

Furthermore, in line with the nature of ainput_iterator_tag ourconst_iterator should not offer anoperator--. This, of course,causes problems: a reverse iterator must be able to use the iterator'soperator-- member. This can easily be solved by stashing the iterator'soperator-- in the iterator's private section, and declaringstd::reverse_iterator<(const_)iterator> its friend (note that declaring a(const_)reverse_iterator that is derived fromstd::reverse_iteratordoesn't solve the issue: it isstd::reverse_iterator that calls theiterator'soperator--, not a class that is derived from it).

To keep the const-ness of dereferencingconst_iterator iteratorsusing specification can added to theconst_iterator class. Theusing specifications provided byData::iterator are inherited byData::const_iterator, butData::const_iterator may also specify:

    using const_pointer   = value_type const *;    using const_reference = value_type const &;
whereafter itsoperator *() can return aconst_reference.

The elements involved in defining aniterator, const_iterator,reverse_iterator andconst_reverse_iterator for a classData isillustrated in the following example, which only requires the definitions oftheiterator andconst_iterator classes, as these types can be passedto thereverse_iterator template to obtain the correspondingreverse-iterators:

    // compile only: members only declared, not implemented#include <string>#include <iterator>#include <iostream>struct Data{    class iterator;    class const_iterator;    using reverse_iterator = std::reverse_iterator<iterator>;    using const_reverse_iterator = std::reverse_iterator<const_iterator>;    private:        std::string *d_data;        size_t d_n;    public:        // ...};struct Data::iterator{    using iterator_category = std::bidirectional_iterator_tag;    using difference_type   = std::ptrdiff_t;    using value_type        = std::string;    using pointer           = value_type *;    using reference         = value_type &;    friend class Data;    private:        pointer d_current;    public:        iterator() = default;        iterator &operator++();        iterator &operator--();        std::string &operator*();    private:        iterator(std::string *data, size_t idx);        friend class std::reverse_iterator<iterator>;};bool operator==(Data::iterator const &lhs, Data::iterator const &rhs);struct Data::const_iterator: public Data::iterator{    using const_pointer   = value_type const *;    using const_reference = value_type const &;    friend class Data;    friend class std::reverse_iterator<const_iterator>;    const_iterator() = default;    const_iterator &operator++();    const_iterator &operator--();    const_reference operator*() const;    // or, in this case: std::string const &operator*() const;    private:        const_iterator(std::string const *data, size_t idx);};bool operator==(Data::const_iterator const &lhs,                Data::const_iterator const &rhs);void demo(){    Data::iterator iter;    Data::reverse_iterator riter(iter);    std::cout << *riter << '\n';    Data::const_iterator citer;    Data::const_reverse_iterator criter(citer);    std::cout << *criter << '\n';};




[8]ページ先頭

©2009-2025 Movatter.jp