searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

C++Traits技法简单介绍及使用

2023-09-27 03:15:57
11
0

一、traits简介

       traits 是一种技法,简单说就是提取传入对象的对应返回类型。STL的算法和容器是分离的,两者通过迭代器链接。所以算法的实现并不会知道被传进来什么。traits相当于在接口和实现之间加一层封装,来隐藏一些细节并协助调用合适的方法,并附带上一些技巧(偏特化等)。

若使用迭代器时,需要使用其所指之物的型别,即算法中需要声明一个迭代器所指之型别的对象时,该怎么办呢?

1,使用 function template 的参数推导机制。

       加一层封装之后,只需传入对象的引用即可,无需每一次都显式的指出迭代器指向对象的型别。但是,function template只能推导参数,无法推导函数的返回值类型。

2,为 class type 声明内嵌型别 

       通过声明内嵌型别,函数 function template就可以根据该型别返回正确型别的返回值。但是,不是所有的迭代器都是 class type的,原生指针就不行!怎么办呢?

3,偏特化 -- 在特化的基础上加一些限制

       func在调用 I 的时候,首先把 I 传到traits中,然后 traits 匹配到最合适的 value_type(traits会优先匹配最特别的版本)。这样当传入一个原生指针的时候,首先匹配的是带有<T*>的偏特化版本,这样 value_type就是T,而不是没有事先声明的I::value_type。这样函数就可以使用 typename iterator_traits<I>::value_type来知道返回类型。

二、  traits的简单使用

1,使用traits在编译期就完成程序的分流,提高程序执行效率。

 假设我们针对不同的迭代器类型编写了不同的处理函数,并对外暴露了一个公共接口。在该接口接收到参数时,它需要:

  • 判断参数属于哪一类迭代器
  • 根据迭代器类别调用相应的函数

      但是,上述判断迭代器的操作发生在执行期,若我们能够在编译期就选择好正确的版本,就可以提高程序效率。通过重载函数,将迭代器类型传入,并对外暴露唯一的接口。

      traits 一方面在面对不同的输入类时,能够找到合适的返回型别;另一方面,当型别对应有不同的实现函数时,能起到一个提取型别然后分流的作用。

2,代码示例

#include <iostream>
using namespace std;

/*先定义一些tag*/
struct A {};
struct B : A{}; 

/*假设有一个未知类*/
template <class AorB>
struct unknown_class {
    typedef AorB return_type;
};

/*特性萃取器*/
template <class unknown_class>
struct unknown_class_traits {
    typedef typename unknown_class::return_type return_type;
};

/*特性萃取器 —— 针对原生指针*/
template <class T>
struct unknown_class_traits<T*> {
    typedef T return_type;
};

/*特性萃取其 —— 针对指向常数*/
template <class T>
struct unknown_class_traits<const T*> {
    typedef const T return_type;
};


/*决定使用哪一个类型*/
template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
return_type(unknown_class) {
    typedef typename unknown_class_traits<unknown_class>::return_type RT;
    return RT();
}

template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
__func(unknown_class, A) {
    cout << "use A flag" << endl;
    return A();
}

template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
__func(unknown_class, B) {
    cout << "use B flag" << endl;
    return B();
}

template <class unknown_class, class T>
T
__func(unknown_class, T) {
    cout << "use origin ptr" << endl;
    return T();
}

template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
func(unknown_class u) {
    typedef typename unknown_class_traits<unknown_class>::return_type return_type;
    return __func(u, return_type());
}

int main() {
    unknown_class<B> b;
    unknown_class<A> a;
    //unknown_class<int> i;
    int value = 1;
    int *p = &value;
    A v1 = func(a);
    B v2 = func(b);
    int v3 = func(p);
}
0条评论
作者已关闭评论
胡****星
4文章数
0粉丝数
胡****星
4 文章 | 0 粉丝
原创

C++Traits技法简单介绍及使用

2023-09-27 03:15:57
11
0

一、traits简介

       traits 是一种技法,简单说就是提取传入对象的对应返回类型。STL的算法和容器是分离的,两者通过迭代器链接。所以算法的实现并不会知道被传进来什么。traits相当于在接口和实现之间加一层封装,来隐藏一些细节并协助调用合适的方法,并附带上一些技巧(偏特化等)。

若使用迭代器时,需要使用其所指之物的型别,即算法中需要声明一个迭代器所指之型别的对象时,该怎么办呢?

1,使用 function template 的参数推导机制。

       加一层封装之后,只需传入对象的引用即可,无需每一次都显式的指出迭代器指向对象的型别。但是,function template只能推导参数,无法推导函数的返回值类型。

2,为 class type 声明内嵌型别 

       通过声明内嵌型别,函数 function template就可以根据该型别返回正确型别的返回值。但是,不是所有的迭代器都是 class type的,原生指针就不行!怎么办呢?

3,偏特化 -- 在特化的基础上加一些限制

       func在调用 I 的时候,首先把 I 传到traits中,然后 traits 匹配到最合适的 value_type(traits会优先匹配最特别的版本)。这样当传入一个原生指针的时候,首先匹配的是带有<T*>的偏特化版本,这样 value_type就是T,而不是没有事先声明的I::value_type。这样函数就可以使用 typename iterator_traits<I>::value_type来知道返回类型。

二、  traits的简单使用

1,使用traits在编译期就完成程序的分流,提高程序执行效率。

 假设我们针对不同的迭代器类型编写了不同的处理函数,并对外暴露了一个公共接口。在该接口接收到参数时,它需要:

  • 判断参数属于哪一类迭代器
  • 根据迭代器类别调用相应的函数

      但是,上述判断迭代器的操作发生在执行期,若我们能够在编译期就选择好正确的版本,就可以提高程序效率。通过重载函数,将迭代器类型传入,并对外暴露唯一的接口。

      traits 一方面在面对不同的输入类时,能够找到合适的返回型别;另一方面,当型别对应有不同的实现函数时,能起到一个提取型别然后分流的作用。

2,代码示例

#include <iostream>
using namespace std;

/*先定义一些tag*/
struct A {};
struct B : A{}; 

/*假设有一个未知类*/
template <class AorB>
struct unknown_class {
    typedef AorB return_type;
};

/*特性萃取器*/
template <class unknown_class>
struct unknown_class_traits {
    typedef typename unknown_class::return_type return_type;
};

/*特性萃取器 —— 针对原生指针*/
template <class T>
struct unknown_class_traits<T*> {
    typedef T return_type;
};

/*特性萃取其 —— 针对指向常数*/
template <class T>
struct unknown_class_traits<const T*> {
    typedef const T return_type;
};


/*决定使用哪一个类型*/
template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
return_type(unknown_class) {
    typedef typename unknown_class_traits<unknown_class>::return_type RT;
    return RT();
}

template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
__func(unknown_class, A) {
    cout << "use A flag" << endl;
    return A();
}

template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
__func(unknown_class, B) {
    cout << "use B flag" << endl;
    return B();
}

template <class unknown_class, class T>
T
__func(unknown_class, T) {
    cout << "use origin ptr" << endl;
    return T();
}

template <class unknown_class>
inline typename unknown_class_traits<unknown_class>::return_type
func(unknown_class u) {
    typedef typename unknown_class_traits<unknown_class>::return_type return_type;
    return __func(u, return_type());
}

int main() {
    unknown_class<B> b;
    unknown_class<A> a;
    //unknown_class<int> i;
    int value = 1;
    int *p = &value;
    A v1 = func(a);
    B v2 = func(b);
    int v3 = func(p);
}
文章来自个人专栏
GB28181/C++
4 文章 | 1 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0