C++模板之trait与模板技巧


一:trait

1.Fixed Traits

  • 主要是构造适应各种类型的函数
  • Fixed主要指,一旦定义了这个分离的 trait,则无法在算法中进行改写。
  • value trait 是用于针对型别进行 value 方面的操作,如 zero(),它是 fixed trait 和下面的另外一种参数化萃取的一部分。
template 
class accumulation_traits;

template <>
class accumulation_traits {
public:
    typedef int AccT;
    static AccT zero() { return 0; }
};

template <>
class accumulation_traits {
public:
    typedef int AccT;
    static AccT zero() { return 0; }
};

template <>
class accumulation_traits {
public:
    typedef long AccT;
    static AccT zero() { return 0; }
};

template <>
class accumulation_traits {
public:
    typedef double AccT;
    //static const double zero = 0.0;   //complier error
   static AccT zero() { return 0; }
};

template 
inline 
typename accumulation_traits::AccT accum(T const* beg, T const* end)
{
    typedef typename accumulation_traits::AccT AccT;
    AccT total = accumulation_traits::zero();
    while(beg != end){
        total += *beg;
        ++beg;
    }
    return total;
}
测试代码:
int main()
{
    int num[] = {1, 2, 3, 4, 5}; 
    std::cout<<"average="<(&num[0], &num[5])<不能在算法中进行改写。然而,有些情况下我们需要对 trait 进行改写。例如:我们偶然发现可以对一组 char 求和,然后需要把他们安全地放在 char 类型的变量里面,而不是经由写好的萃取变为 int 做处理,这没必要且效率不高。 
    

从原则上讲,参数化 trait 主要目的在于:添加一个有缺省值的模板参数,而且该缺省值是由我们前面介绍的 trait 模板决定的。在这种具有缺省值的情况下,用户可以不需要提供这个额外的模板是惨。但是对于特殊用户,你自己就可以定义自己的 trait 方案。唯一不足之处在于: 不能对函数模板预设缺省模板实参

所以我们通过把算法 实现为一个类,就可以绕开这个不足。这同时说明:除了函数模板,类模板也可以很容易使用 trait。不过,类模板不能对参数进行演绎必须由客端显示提供模板实参(比如stack),所以可以把它封装为函数来简化接口。
template  >
class accum_class {
public:
    static typename AT::AccT accum(const T* beg, const T* end) {
        typename AT::AccT total = AT::zero();
        while(beg != end) {
            total += *beg;
            ++beg;
        }   
        return total;
    }   
};

template 
inline
typename accumulation_traits::AccT accum(const T* beg, const T* end)
{
    return accum_class::accum(beg, end);
}

template    //其中第一个参数就是 trait 方案,有默认值,也可由用户提供
inline
typename Traits::AccT accum(const T* beg, const T* end)
{
    return accum_class::accum(beg, end);
}

int main()
{
    int num[] = {1, 2, 3, 4, 5}; 
    std::cout<<"average="< >(&name[0],&name[length])/length<



二:其他技巧

(以下转自: C++ Template之技巧性基础知识

1.对于T是自定义类型的,如果存在子类型则需要在模版内部加上typename

示例代码:

1
2
3
4
5
template < typename  T>
class  Myclass
{
     typename  T::SubType *ptr; //需要加上typename不然编译器可能误解为是静态对象SubType和ptr相乘
};

2.类模版里对基类成员函数的调用使用BASE::exit();和this->,避免调用的是外部全局函数,但是在vc6.0上面这条规则是先调用的BASE里面的函数。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include
#include
#include
using  namespace  std;
 
 
void  exit ()
{
     cout <<  "hello world"  <
}
template  < typename  T>
class  BaseMyclass
{
public :
     void  exit ()
     {
         cout <<  "Base"  <
     }
};
 
template  < typename  T>
class  Myclass: public  BaseMyclass
{
     
public :
 
     void  foo()
     {
         exit ();
     }
};
 
 
int  main()
{
Myclass< int > m1;
m1.foo();
     
     return  0;
}

 3.成员模板,由于两种不同的类型之间的赋值不能使用类本身的接口,所以需要重新设计接口。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include
#include
#include
using  namespace  std;
 
template  < typename  T>
class  Stack
{
     public :
         template  < typename  T2>
         Stack& operator=(Stack  const  &);
};
template  < typename  T>
template  < typename  T2>
Stack& Stack::operator=(Stack  const  &op2)
{
 
}
 
int  main()
{
     
     return  0;
}

 调用时首先通过显式指定的模板类型实例化一个类,然后通过实例化一个成员函数,只是现在的成员函数是模板函数,然后根据调用的参数实例化成一个成员函数。

4.模板的模板参数,实际上是一个模板类,上面的成员模板实际上是一个模板函数。

代码示例:

1
2
3
4
5
6
template  < typename  T, template < typename  T>  class  CONT = vector>
class  Stack
{
     public :
     
};

5.零初始化,因为模板不知道类型所以我们无法用任何一个常量来初始化模板参数定义的变量。但是可以如下例子进行初始化

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include
#include
#include
using  namespace  std;
 
template  < typename  T>
void  foo()
{
     T x = T();
}
 
template  < typename  T>
class  Myclass
{
     private :
         T x;
     public :
         Myclass():x(T()){}
};
 
int  main()
{
     foo< int >();
     Myclass< int > m1;
     return  0;
}

6.使用字符串作为函数模板的实参若是引用则需要字符串长度完全匹配

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template  < typename  T>
inline  T&  const  max(T  const &  a,T  const &  b)
{
     return  a > b ? a:b;
}
 
 
 
int  main()
{
     cout << max( "apple" , "peach" )< //OK
     cout << max( "apple" , "tomato" )< //ERROR
     return  0;
}

 若不为引用则不需要字符串长度完全一样,原因是参数会转换成字符串指针类型,属于相同的类型

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include
#include
#include
using  namespace  std;
 
template  < typename  T>
inline  T max(T a,T b)
{
     return  a > b ? a:b;
}
 
 
 
int  main()
{
     cout << max( "apple" , "peach" )< //OK
     cout << max( "apple" , "tomato" )< //OK
     return  0;
}

 字符串作为实参的测试示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template  < typename  T>
void  ref(T  const & x,T  const & y)
{
     cout <<  "x int the ref is "  <<  typeid (x).name() <
}
 
template  < typename  T>
void  nonref(T x)
{
     cout <<  "x int the noref is "  <<  typeid (x).name() <
}
int  main()
{
     ref( "hello" );
     nonref( "hello" );
     
     return  0;
}






你可能感兴趣的:(C/C++)