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 內某個元素的位址

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值