C++ template

C++ template


1. 模板参数推导
 
2. typename与class的区别(C++ Primer E4, P532)(Effective C++, P203)
1) 在函数模板形参数表中,关键字typename、class具有相同的含义。(使用关键字class,原是为了避免增加新的关键字,然而最终还是不得不引入一个新

的关键字, The C++ Standard Library, P9)
2) 考虑下面的函数
    template <class Parm, class U>
    Parm fcn(Parm* array, U value)
    {
        Parm::size_type * p; //两个数相乘
    }
默认情况下,编译器假定这样的名字指定数据成员,而不是类型,如果希望编译器将size_type当作类型,则必须显示告诉编译器这样做:
    template <class Parm, class U>
    Parm fcn(Parm* array, U value)
    {
        typename Parm::size_type * p; //现在p是一个指针
}
3) typename必须作为嵌套从属类型名称的前缀词这一规则的例外是,typename不可以出现在base class list 内的嵌套从属类型名称之前,也不可在成

员初始化列表中作为base class修饰词。例如:
template<typename T>
class Derived : public Base<T>::Nested
{
public:
    explicit Derived(int x) : Base<T>::Nested(x)
    {
        typename Base<T>::Nested temp;
    }
};
3. typename的使用条件(C++ PL题解, P121)
1) 该名字是限定的,即它包含一个作用域解析运算符”::”;
2) 它出现在一个模板里
3) 被限定的名字有一个万分在这个作用域解析运算符的左边,而且它依赖于模板的形式参数
4) 它确实指称一个类型
5) 它没有用在基类型的表里,也没有作为某个构造函数初始化列表里的一个项
4. 链接时的编译错误(C++ Primer E4, P535)
5. 模板实参推断(C++ Primer E4, P537)
1) 编译器只会执行两种转换:
 const转换:接受const引用(指针)的函数可以分别用非const对象的引用(指针)来调用,无须产生新的实例化。
 数组(函数)到指针的转换:如果模板参数不是引用类型,则对数组或函数类型的实参应用常规指针转换。
    template <typename T> T fObj(T, T);
    template <typename T> T fRef(const T&, const T&);
    string s1("a value");
    const string s2("another value");
    fObj(s1, s2);  //ok:忽略const
    fRef(s1, s2);  //ok:s1转换成const引用
    int a[10], b[42];
    fObj(a, b);    //ok:调用fObj(int*, int*);
fRef(a, b);    //error:引用类型不会转换到指针
2) 模板实参推断与函数指针
可以使用函数模板对函数指针进行初始化或赋值。
    template <typename T> T compare(const T&, const T&) { return T(); };
    int (*pf1) (const int&, const int&) = compare;//赋值
    void fun(int(*) (const string&, const string&));
    func(compare);//初始化
    func(comapre<string>);//显示初始化,可以可以二义性(C++ Primer E4, P542)

6. 模板作用域中模板类型的引用(C++ Primer E4, P547)
在类本身的作用域内部,可以使用类模板的非限定名,实质上,编译器推断,当我们引用类的名字时,引用的是同一版本。
编译器不会为类中使用的其他模板的模板形参进行这样的推断,因此,在声明其他类的对象时,必须指定类型形参。
7. 类模板的成员函数、成员模板(C++ Primer E4, P548)
1) 在类外部定义成员函数:
template <class T> ret-type Queue<T>::member-name()
2) 在类外部定义成员模板:(C++ Primer E4, P557)
template<class T> template<class Iter>
void Quere<T>::assign(Iter beg, Iter end)
{
    destroy();
    copy_elems(beg, end);
}
3) 在类特化外部定义成员时,成员之前不能加template<>标记。
void Queue<const char*>::push(const char* val)
{
    return real_queue.push(val);
}
8. 实例化(C++ Primer E4, P536)
1) 模板在使用时将进行实例化,类模板在引用实际模板类类型时实例化;函数模板在调用它或用它对函数指针进行初始化或赋值时实例化。
2) 实际处理template时,面对template function, 你必须先提供它的某个实例,然后才能调用,如此方可通过编译。所以目前唯一能够让”template的

运用”具有可移植性的方式,就是在头文件中以inline function实现template function.(The C++ Standard Library, P10)
9. 函数模板的特化(C++ Primer E4, P567)
1) 对具有同一模板实参集的同一模板,程序不能既有显示特化又有实例化,例如:
template<typename T>
int compare(const T& lhs, const T& rhs)
{
    return true;
}

int main( void )
{
    const char* lhs = "hello";
    const char* rhs = "world";
    cout << compare(lhs, rhs);  //注:不能用compare("hello", "world"),否则会调用
                      //typedef char CHAR6[6];
                      //template<>
                      //int compare<CHAR6>(const CHAR6& lhs, const CHAR6& rhs);
    return 0;
}

template<>
int compare<const char*>(const char* const& lhs, const char* const& rhs)
{
    return false;
}
将出现编译错误:error C2908: explicit specialization; 'int __cdecl compare(const char *const & ,const char *const & )' has already been

instantiated from the primary template
10. 特化一个函数模板如std::swap(Effective C++ E3, P106)
1) 以下针对类widget特化std::swap(通常我们可以为标准template制造特化版本,Effective C++ E3, P107)
struct widget {};
namespace std
{
    template<>
    void swap<widget>(widget& lhs, widget& rhs)
    {
        cout << "特化版swap/n";
    }
}

2) 更一般的做法是让特化版本调用widget的public成员函数,这也是标准容器的做法:
struct widget
{
    void swap(widget& rhs)
    {
        cout << "widget::swap/n";
    }
};

namespace std //注意这里
{
    template<>
    void swap<widget>(widget& lhs, widget& rhs)//这是一个特化
    {
        cout << "特化版swap/n";
        lhs.swap(rhs);
    }
}

3) 如果widget是一个模板,这样做:
namespace widgetspace
{
    template<typename T>
    struct widget
    {
        void swap(widget& rhs)
        {
            cout << "widget::swap/n";
        }
    };   
   
    template<typename T>
    void swap(widget<T>& lhs, widget<T>& rhs)//这是一个函数模板重载
    {
        cout << "特化版swap/n";
        lhs.swap(rhs);
    }
}

int main( void )
{
    using std::swap;
    widgetspace::widget<int> a, b;
    swap(a, b);
}
(注:系统不会执行我们的特化版本,除非显示调用widgetspace::swap, 还不明白原因)
11. 类模板的特化(C++ Primer E4, P569)
1) 特化类:
template<> class Queue<const char*> {};
2) 可以不特化类,只特化成员:
template<>
void Queue<const char*>::push(const char* const& val) {}
12. 类模板偏特化(C++ Primer E4, P570)
1) VC6不支持模板的部分特化
2) 部分特化在程序中使用时隐式实例化
3) 部分特化可以具有与通用类模板完全不同的成员集合
13. 模板参数约束条件(C++ PL题解, P117)
1) 两个类型不能相同
template<typename T> struct TypeHolder {};//使用非类类型合法

template<typename T, typename U>
struct NonEqualTypes : TypeHolder<T>, TypeHolder<U> {};//不能从同一个类继承

2) 两个类型具有继承关系
#define INHERITS_CLASSTYPE(D, B) { /
    D* derived = 0; /
    B* base = derived; /
    NonEqualTypes<D*, void*> dummy; /
}

3) 要有可访问的默认构造函数
#define ACCESSIBLE_DEFAULT_CONSTRUCTOR(T) { /
    T x; /
}

4) 要有可访问的拷贝构造函数,不要求有默认构造函数(C++不能有局部函数,但可以有局部类)
#define ACCESSIBLE_COPYCTOR(T) { /
    struct Dummy { /
        void f(const T& x) { new T(x); } /
    }; /
}

5) 非void类型(不能用void作函数参数, 参见C++ PL题解, P120)
#define NONE_VOID_TYPE(T) { /
    void foo(int, T); /
}
6) 更精简的约束: (Bjarne Stroustrup 的FAQ, P16)
一行命名要检查的约束,和要检查的类型
一行列出指定的要检查的约束(constraints()函数)
一行提供触发检查的方法(通过构造函数)
template<class T, class B> struct Derived_from {
    static void constraints(T* p) { B* pb = p; }
    Derived_from() { void(*p)(T*) = constraints; }
};
template<class T1, class T2> struct Can_copy {
    static void constraints(T1 a, T2 b) { T2 c = a; b = a; }
    Can_copy() { void(*p)(T1,T2) = constraints; }
};
template<class T1, class T2 = T1> struct Can_compare {
    static void constraints(T1 a, T2 b) { a==b; a!=b; a<b; }
    Can_compare() { void(*p)(T1,T2) = constraints; }
};
template<class T1, class T2, class T3 = T1> struct Can_multiply {
    static void constraints(T1 a, T2 b, T3 c) { c = a*b; }
    Can_multiply() { void(*p)(T1,T2,T3) = constraints; }
};

// 典型的“元素必须继承自Mybase*”约束:
template<class T> class Container : Derived_from<T,Mybase> {
    // ...
};
7) 探测可转换性和可继承性(Modern C++ Design, P35)
template<class T, class U>
class Conversion
{
    typedef char Small;
    typedef Big { char dummy[2]; };
    static Small Test(U);
    static Big(...);
    static T MakeT();//稻草人
public:
    enum { exists = sizeof(Test(MakeT())) == sizeof(Small) };
};

int main()
{
    cout << Conversion<double, int>::exists << '/t'
        << Conversion<char, char*>::exists << endl;
}
14. 函数模板的使用__001(深入C++对象模型, P93)
class Point3d
{
public:
    float x;
    float y;
    float z;
};

template< class class_type, class data_type1, class data_type2 >
char* access_order(data_type1 class_type::*mem1, data_type2 class_type::*mem2)
{
    assert(mem1 != mem2 );
    return
        mem1 < mem2
        ? "member 1 occurs first"
        : "member 2 occurs first";
}

int main( void )
{
    access_order( &Point3d::z, &Point3d::y); //不能编译通过
    return 0;
}
15. 学习处理模板化基类内的名称(Effective C++ E3, P207)
在Effective中说,下面的代码将不能通过编译,而事实上在VC6上是可以的。
template<typename T>
struct MsgSender
{
    void SendClear() {}  //发送明文
    void SendSecret() {} //发送密文
};

template<typename T>
struct LoggingMsgSender : public MsgSender<T>
{
    void SendClearMsg()
    {
        SendClear();//书上说,这个SendClear不存在
    }
};

template<> //全特化
struct MsgSender<int>
{
    void SendSecret() {} //没有实现Sendclear()
};

int main( void )
{
    LoggingMsgSender<int> oo;
    //oo.SendClearMsg();
    return 0;
}

书上的解释是:它(编译器)知道base class template(这里是MsgSender)有可能被特化(针对int进行特化),而那个特化版本可能不提供和一般性template相同

的接口。因此它往往拒绝在templatized base class(模板化基类,本例的MsgSender<T>)内寻找继承而来的名称(本例的SendClear)。继承不像以前那般畅行

无阻了。
书上提供了三种方法:
1) 在base class函数调用动作之前加上”this->”:
template<typename T>
struct LoggingMsgSender : public MsgSender<T>
{
    void SendClearMsg()
    {
        this->SendClear();
    }
};

2) 使用using声明式
template<typename T>
struct LoggingMsgSender : public MsgSender<T>
{
    using MsgSender<T>::SendClear;
    void SendClearMsg()
    {
        this->SendClear();
    }
};
3) 明白指出被调用的函数位于base class内
template<typename T>
struct LoggingMsgSender : public MsgSender<T>
{
    void SendClearMsg()
    {
        MsgSender<T>::SendClear();
    }
};
16. 需要类型转换时请为模板定义成非成员函数(Effective C++ E3, P222)
设法让下面的代码通过编译:(注:在class内部声明非成员函数的唯一办法是令它成为一个friend)
template<typename T>
struct Rational
{
    Rational(const T& n = 0) {}
};

int main( void )
{
    Rational<int> a, c;
    c = a * 100;  
    return 0;
}
上述代码不能通过的原因:template实参推导过程中从不将隐式类型转换函数纳入考虑,绝不。

尝试一
template<typename T>
struct Rational
{
    Rational(const T& n = 0) {}
    friend const Rational operator* (const Rational& lhs, const Rational& rhs);
};

template<typename T>
const Rational<T> operator* (const Rational<T>& lhs, const Rational<T>& rhs)
{
    return Rational<T>();
}
这样做的根据是,基于一个事实:template class内的friend声明式可以指涉某个特定函数。因为当对象a被声明为一个Rational<int>,class Rational<int>

于是被实例化,而作为过程的一部分,friend函数operator*(接受Rational<int>参数)也被自动声明出来。后者是一个函数而非函数模板,因此编译器可在调

用它时使用隐式转换函数。
但那个函数只被声明于Rational内,并没有被定义出来。我们意图令此class外部的operator* template提供定义式,但是行不通——如果我们自己声明了一个

函数,就有责任定义那个函数。既然没有提供定义式,连接器当然找不到它。
结论:
template<typename T>
struct Rational
{
    Rational(const T& n = 0) {}
    friend const Rational operator* (const Rational& lhs, const Rational& rhs)
    {
        return Rational();
    }
};
17. 成员模板(The C++SLibrary, P12)
1) 成员模板不能为virtual,也不能有缺省参数。
2) 请注意,现在,assign()的参数x和*this的类型不同,所以不能再直接存取MyClass<>中的private 成员和protected成员了,取而代之的是,必须使

用getValue()之类的东西。
template <class T> class MyClass
{
    T value;
public:
    template <class X> void assign(const MyClass<X>& x)
    {
        value = x.getValue();
    }
    T getValue() const
    {
        return value;
    }
};
18. 以下不能作为模板参数(C++ Template, P110)
null pointer 常数
浮点数(floating-point numbers)
字符串字面常数(string literals)
不能使用array 内某个元素的地址

template<typename T, T nontype_param> class C;
class Base {
public:
int i;
} base;
C<int&, base.i>* err2; // ERROR:成員變數不被考慮
int a[10];
C<int*, &a[0]>* err3; // ERROR:不能使用array 內某個元素的位址

 

C++ templates are a powerful feature of the C++ programming language that allow generic programming. Templates enable the creation of functions and classes that can work with different data types without the need for separate implementations for each data type. Templates are defined using the keyword "template" followed by a list of template parameters enclosed in angle brackets "< >". The template parameters can be either type parameters or non-type parameters, depending on whether they represent a data type or a value. For example, a type parameter might be used to specify the data type of a container class, while a non-type parameter might be used to specify the size of an array. Here is an example of a simple function template that returns the maximum of two values: ```c++ template<typename T> T max(T a, T b) { return a > b ? a : b; } ``` In this example, the "typename" keyword is used to indicate that T is a type parameter. The function can be used with any data type for which the ">" operator is defined. Templates can also be used to define class templates, which are similar to regular classes but can work with different data types. Here is an example of a simple class template for a stack: ```c++ template<typename T> class Stack { public: void push(T value); T pop(); private: std::vector<T> data_; }; template<typename T> void Stack<T>::push(T value) { data_.push_back(value); } template<typename T> T Stack<T>::pop() { T value = data_.back(); data_.pop_back(); return value; } ``` In this example, the class template is defined with a single type parameter T. The member functions push and pop are defined outside the class definition using the scope resolution operator "::". Templates are a powerful tool that can greatly simplify code and make it more reusable. However, they can also be complex and difficult to debug. It is important to use templates judiciously and to thoroughly test them with a variety of data types.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值