C++ Type function(9)---《C++ Templates》

In C and C++, most functions can be called value functions, which accept certain values as independent variables and return a value as a result. Now we use templates technology to make so-called type functions: accept a type as an independent variable, and produce a type or constant as a result.
zizeof is a very useful type function, where class templates can act as a type function, in which case the parameter is template parameters, and the result is extracted into a member type or member constant.

#include <stddef.h>
#include <iostream>

template <typename T>
class TypeSize{
public:
    static size_t const value=sizeof(T);
};
int main(){
    std::cout<<"TypeSize<int>::value="<<TypeSize<int>::value<<std::endl;
    return 0;
}   

Determining element type

We have multiple container templates, and we want to have a type function that can output element types (element types) after accepting a container type, which can be achieved through partial specialization.

#include <vector>
#include <list>
#include <stack>
#include <iostream>
#include <typeinfo>

template <typename T>
class ElementsT;

template <typename T>
class ElementT<std::vector<T> >{
public:
    typedef T Type;
};
template <typename T>
class ElementT<std::list<T> >{
public:
    typedef T Type;
};
template <typename T>
class ElementT<std::stack<T> >{
public:
    typedef T Type;
};

template <typename T>
void print_element_type(T const& c){
    std::cout<<"Container of "<<typeid(typename ElementT<T>::Type).name()<<" elements.\n";
}
int main(){
    std::stack<bool> s;
    print_element_type(s);
}

Above, we implemented this function through partial specialization without requiring container types to understand type functions. However, in many cases, type function is often designed together with the types that can be implemented on it, making the implementation code easier to simplify:

template <typename C>
class ElementT{
public:
    typedef typename C::value_type Type;
};

How does the value of type function reflect? It allows us to parameterize a template with container type, and does not require that the relevant parameters and other features of element type be provided together.

template <typename T,typename C>
T sum_of_element(C const&c);

If we write like this, we need to specify element type s explicitly in a syntax such as sum_of_elements < int > (list). We can change that to state that:

template <typename C>
typename ElementT<C>::Type sum_of_elements(C const& c);

In this way, element type can have type function to specify.

Verify Class Types

template <typename T>
class IsClassT{
private:
    typedef char One;
    typedef struct{char a[2];} Two;
    template<typename C> static One test(int C::*);
    template<typename C> static Two test(...);
public:
    enum{Yes=sizeof(IsClassT<T>::test<T>(0))==1};
    enum{No!=Yes};
};

template <typename T>
void check(){
    if(IsClass<T>::Yes{
        std::cout<<"IsClassT "<<std::endl;
    }else{
        std::cout<<"!IsClassT "<<std::endl;
    }
}

References and Qualifiers

template <typename T>
void apply(T& arg,void(*func)(T)){
    func(arg);
}
#include <iostream>
void incr(int& a){
    ++a;
}
void print(int a){
    std::cout<<a<<std::endl;
}
int main(){
    int x=7;
    apply(x,print);
    apply(x,incr);
}

There's no problem with the first function call. What about the second? That is, when apply(x,incr) is called, T must be replaced by int &, which means that the type of the first parameter must be int &, which is not a legal C++ type, so a later technical error suggests that if T in T&is replaced by int &, then T&is equivalent to int &.

template <typename T>
class TypeOp{
public:
    typedef T ArgT;
    typedef T BareT;
    typedef T const ConstT;
    typdedef T& RefT;
    typedef T& RefBareT;
    typedef T const& RefConstT;
};
template <typename T>
class TypeOp<T const>//Partial version
{
public:
    typdef T const ArgT;
    typedef T BareT;
    typedef T const ConstT;
    typedef T const& RefT;
    typedef T& RefBareT;
    typedef T const& RefConstT;
};
template <typename T>
class TypeOp<T&>{//Partial Design for references
public:
    typedef T& ArgT;
    typedef typename TypeOp<T>::BareT BareT;
    typedef T const constT;
    typedef T& RefT;
    typedef typename TypeOp<T>::BareT& RefBareT;
    typedef T const& RefConstT;
};
template<>
class TypeOp<void>{
public:
    typedef void ArgT;
    typedef void BareT;
    typedef void const ConstT;
    typedef void RefT;
    typedef void RefBareT;
    typedef void RefConstT;
};
template <typename T>
void apply(typename TypeOp<T>::RefT arg,void(*func)(T)){
    func(arg);
}

Promotion Traits (Feature Extraction of Type Promotion)

template <typename T>
Array<T> operator+(Array<T> const&,Array<T> const&);

But now we want to add two Array s of type char and type int, and we need to decide what type of return value is.

template <typename T1,typename T2>
Array<???> operator+(Array<T> const&,Array<T2> const&);

How do we solve the above template function??? What about it? Promotion traits template can be used to solve:

template <typename T1,typename T2>
Array<typename Promotion<T1,T2::ResultT> operator+ (Array<T1> const&,Array<T2> const&);
template <typename T1,typename T2>
typename Promotion<Array<T1>,Array<T2> >::Result operator+ (Array<T1> const&,Array<T2> const&);

template <bool c,typename Ta,typename Tb>
class IfThenElse;

template <typename Ta,typename Tb>
class IfThenElse<true,Ta,Tb>{
public:
    typedef Ta ResultT;
}
template <typename Ta,typename Tb>
class IfThenElse<false,Ta,Tb>{
public:
    typedef Tb ResultT;
};

template <typename T1,typename T2>
class Promotion{
public:
    typedef typename IfThenElse<(sizeof(T1)>sizeof(T2)),T1,typename IfThenElse<(sizeof(T1)<sizeof(T2)),T2,void>::ResultT>::ResultT ResultT;
};

//Partial version
template <typename T1,typename T2>
class Promotion<Array<T1>,Array<T2> >{
public:
    typedef Array<typename Promotion<T1,T2>::ResultT> ResultT;
};
template <typename T>
class Promotion<Array<T>,Array<T> >{
public:
    typedef Array<typename Promotion<T,T>::ResultT> ResultT;
};
template <typename T>
class Promotion<T,T>{
public: 
    typedef T ResultT;
};

PS:
The traits templates instances we give are used to determine the attributes of template parameters, which type should be promoted in mixed type operations? Such traits are called property traits. Other traits are responsible for defining how certain types are initialized, which we call policy traits.

Posted by Waldir on Thu, 13 Dec 2018 11:09:39 -0800