Traits技术

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

Traits 技术
 
1. 概念
Trait的中文意思就是特性,Traits就像特性萃取机,榨取不同类的特性,以便能统一处理。Traits依靠显式模板特殊化(explicit template specialization)来把代码中因类型不同而发生变化的片断拖出来,用统一的接口来包装。这个接口可以包含一个C++类所能包含的任何东西:内嵌类型,成员函数,成员变量,作为客户的模板代码可以通过traits模板类所公开的接口来间接访问之。花了点时间,憋了一个示例程序,虽然没什么实际意义,但对于理解traits来说,可能会有帮助。
#include <iostream>
class CM {};
class CA {};
class CIM {};
template< typename T >
class Traits
{
public:
    typedef unsigned int ValueType;
    Traits() : m_rate( 2 ) {}
    ValueType ComputeValue( ValueType value ) { return value * m_rate; }
private:
    ValueType m_rate;
};
template<>
class Traits< CM >
{
public:
    typedef double ValueType;
    Traits() : m_rate( 1.2 ) {}
    ValueType ComputeValue( ValueType value ) { return value * m_rate; }
private:
    ValueType m_rate;
};
template<>
class Traits< CA >
{
public:
    typedef int ValueType;
    Traits() : m_rate( -1 ) {}
    ValueType ComputeValue( ValueType value ) { return value * m_rate; }
private:
    ValueType m_rate;
};
template< typename T >
struct ValueCount
{
    void result()
    {
        Traits< T > traits;
        Traits< T >::ValueType value = static_cast< Traits< T >::ValueType >( 2 );
        std::cout << traits.ComputeValue( value ) << std::endl;
    }
};
int main()
{
    ValueCount< CIM > cim;
    cim.result();
    ValueCount< CM > cm;
    cm.result();
    ValueCount< CA > ca;
    ca.result();
    return 0;
}
代码中的Tratis类对于CA和CM有显式的特化实现,其中ValueType类型和rate与默认的实现不同,在类ValueCount中,利用Tratis对不同类型用一个统一的接口符号处理。程序的运行结果是4,2.4,-2。(这是我的第一个模版程序J)
2.SGI STL 中的 __type_traits
在SGI实现版的STL中,为了获取高效率,提供了__type_traits,用来提取类的信息,比如类是否拥有trival的构造、析构、拷贝、赋值操作,然后跟据具体的信息,就可提供最有效率的操作。以下摘录cygwin的gcc3.3源码,有改动,在<type_traits.h>中。
struct __true_type {};
struct __false_type {};
template <class _Tp>
struct __type_traits {
    typedef __false_type has_trivial_default_constructor;
    typedef __false_type has_trivial_copy_constructor;
    typedef __false_type has_trivial_assignment_operator;
    typedef __false_type has_trivial_destructor;
    typedef __false_type is_POD_type;
};
对于普通类来讲,为了安全起见,都认为它们拥有non-trival的构造、析构、拷贝、赋值函数,POD是指plain old data。接下来对C++的原生类型(bool,int, double之类)定义了显式的特化实现,以double为例:
template<> struct __type_traits<long double> {
    typedef __true_type has_trivial_default_constructor;
    typedef __true_type has_trivial_copy_constructor;
    typedef __true_type has_trivial_assignment_operator;
    typedef __true_type has_trivial_destructor;
    typedef __true_type is_POD_type;
};
还有,对所有的原生指针来讲,它们的构造、析构等操作也是trival的,因此有:
template <class _Tp>
struct __type_traits<_Tp*> {
    typedef __true_type has_trivial_default_constructor;
    typedef __true_type has_trivial_copy_constructor;
    typedef __true_type has_trivial_assignment_operator;
    typedef __true_type has_trivial_destructor;
    typedef __true_type is_POD_type;
};
简化<stl_algobase.h>中copy的部分代码来说明对__type_traits的应用。
template<typename _Tp>
inline _Tp*
__copy_trivial(const _Tp* __first, const _Tp* __last, _Tp* __result)
{
    memmove(__result, __first, sizeof(_Tp) * (__last - __first));
    return __result + (__last - __first);
}
template<typename _Tp>
inline _Tp*
__copy_aux (_Tp* __first, _Tp* __last, _Tp* __result, __true_type)
{ return __copy_trivial(__first, __last, __result); }
template<typename _Tp>
inline _Tp*
__copy_aux (_Tp* __first, _Tp* __last, _Tp* __result, __false_type)
{ 另外处理; }
template<typename _InputIter, typename _OutputIter>
inline _OutputIter
copy (_InputIter __first, _InputIter __last, _OutputIter __result)
{
    typedef typename iterator_traits<_InputIter>::value_type _ValueType;
    typedef typename __type_traits<_ValueType>::has_trivial_assignment_operator _Trivial;
    return __copy_aux(__first, __last, __result, _Trivial());
}
Copy函数利用__type_traits判断当前的value_type是否有trival的赋值操作,如果是,则产生类__true_type的实例,编译时选择__copy_trivial函数进行memmove,效率最高。如果是non-trival的赋值操作,则另作处理,效率自然低些。__true_type和__false_type之所以是类,就因为C++的函数重载是根据类型信息来的,不能依据参数值来判别。使用SGI STL时,可以为自己的类定义__type_traits显式特化版本,以求达到高效率。
3. STL 中的 iterator_traits
iterator_traits提供5个特性的提取,至于为什么是5个以及iterator的分类,以后再议。代码包含在<stl_iterator_base_types.h>中。
template<typename _Category, typename _Tp, typename _Distance = ptrdiff_t,
typename _Pointer = _Tp*, typename _Reference = _Tp&>
struct iterator
{
    typedef _Category iterator_category;
    typedef _Tp value_type;
    typedef _Distance difference_type;
    typedef _Pointer pointer;
    typedef _Reference reference;
};
template<typename _Iterator>
struct iterator_traits {
    typedef typename _Iterator::iterator_category iterator_category;
    typedef typename _Iterator::value_type value_type;
    typedef typename _Iterator::difference_type difference_type;
    typedef typename _Iterator::pointer pointer;
    typedef typename _Iterator::reference reference;
};
原生指针(如int*,double*)也是iterator,但它不是类,无法提取出value_type,所以要对原生指针和const原生指针进行显式特化。
template<typename _Tp>
struct iterator_traits<_Tp*> {
    typedef random_access_iterator_tag iterator_category;
    typedef _Tp value_type;
    typedef ptrdiff_t difference_type;
    typedef _Tp* pointer;
    typedef _Tp& reference;
};
template<typename _Tp>
struct iterator_traits<const _Tp*> {
    typedef random_access_iterator_tag iterator_category;
    typedef _Tp value_type;
    typedef ptrdiff_t difference_type;
    typedef const _Tp* pointer;
    typedef const _Tp& reference;
};
现在,对于所有的iterator都可以正确的提取出以上5个特性。
下面解释iterator_category,iterator共分为5类,input_iterator,output_iterator,forward_iterator,bidirectional_iterator,random_access_iterator。其中forward_iterator是input_iterator和output_iterator的强化(refinement),bidirectional_iterator是forward_iterator的强化,random_access_iterator是bidirectional_iterator的强化。由于5种iterator的性质的同异,需要对它们的种类进行区分,制定特化的函数以达到最优的效率,就像上一节的type_traits一样。需要强调的是,强化不是继承,C++重载机制支持对继承类的正确选择。由于不同iterator有共同的操作,在iterator_category中建立继承关系可以简化大部分特化函数的实现。
struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
由iterator_category带来的效率优化,可由在<stl_iterator_base_funcs.h>内的两个函数看出,一个是用来计算两个iterator的距离__distance,一个是将iterator累进n次的__advance。列举__distance的代码如下,有改动。
template<typename _InputIterator>
inline typename iterator_traits<_InputIterator>::difference_type
__distance(_InputIterator __first, _InputIterator __last,
input_iterator_tag)
{
    typename iterator_traits<_InputIterator>::difference_type __n = 0;
    while (__first != __last) {
        ++__first; ++__n;
    }
    return __n;
}
template<typename _RandomAccessIterator>
inline typename iterator_traits<_RandomAccessIterator>::difference_type
__distance(_RandomAccessIterator __first, _RandomAccessIterator __last,
random_access_iterator_tag)
{ return __last - __first; }
template<typename _Iter>
inline typename iterator_traits<_Iter>::iterator_category
__iterator_category(const _Iter&)
{ return typename iterator_traits<_Iter>::iterator_category(); }
template<typename _InputIterator>
inline typename iterator_traits<_InputIterator>::difference_type
distance(_InputIterator __first, _InputIterator __last)
{ return __distance(__first, __last, __iterator_category(__first)); }
4.  traits 技术还有很夸张的应用。《 C++  设计新思维:范型编程与设计模式之应用》中有体现,或者,泛型编程还得依赖 traits 技术,也许以后的 C++ 会从语言特性上支持 traits

explicit specialization of non-template 相信很多人在使用C++模板特例化都遇到过这个错误,很多人不知道原因甚至一头雾水,接下来我就用这篇文章解释下出现这个错误的原因。模板的所有特例化版本都应声明在与主模板相同的名字空间中。如果某个特例化版本被使用,而且它是显式声明的,则它必须显式定义。换句话说,显式特例化一个模板就意味着编译器不再为此特例化版本生成其它定义。 阅读详情

相关推荐

C++ 中traits 类模板(type traits / customization traits)设计技术深度详解

是一把强大的工具:把类型相关信息/策略移到可特化的模板类里,从而实现编译期可定制、无虚函数开销的多态/策略分发。结合 detection idiom // concepts,可以做到既灵活又安全。最佳实践:提供安全默认、文档化接口、使用表达检测避免脆弱的检测、在需要时用concepts提升可读性和错误信息。

qq_36812406的博客 1752

Template Explicit Specialization (模板显式特化)

模板显式特化(Template Explicit Specialization)是为特定的数据类型提供自定义的实现,而不是使用通用模板版本。这样做的目的是在某些情况下优化性能或提供特定的行为。模板显式特化是C++模板编程中强大的工具,它允许开发者为特定类型提供优化和定制的实现,从而提高性能和灵活性。在使用时应注意保持代码的一致性和可维护性,并通过充分的测试确保特化版本的正确性。联系我。

micro9981的专栏 672

explicit specialization of ‘Race‘ after instantiation ,implicit instantiation first required here。

报错1: E:\project\qt\Pokemon3\PokemonServer\pokemon.cpp:470: error: specialization of ‘Race::Race() [with int N = 0]’ after instantiation Race<0>::Race() : PokemonBase(ATK) ^ 报错2: explicit specialization of ‘Race’ after instantiation implicit instantia

行步至春深 1317

C++ STL的Traits 特征萃取

traits,又被叫做特性萃取技术,说得简单点就是提取“被传进的对象”对应的返回类型,让同一个接口实现对应的功能。因为STL的算法和容器是分离的,两者通过迭代器链接。算法的实现并不知道自己被传进来什么。萃取器相当于在接口和实现之间加一层封装,来隐藏一些细节并协助调用合适的方法,这需要一些技巧(例如,偏特化)。最后附带一个小小的例子,应该能更好地理解 特性萃取。 下面大部分来源于《STL源码剖析》

JackZhangNJU的专栏 1353

C++中的模板特性(Traits

C++中的模板特性(Traits)是一种常见的技术,用于在编译时提取或操作类型信息。Traits类提供了一种灵活且可扩展的方法,用于实现类型特定的行为和元编程。

qq_41328432的博客 1092

Traits技术初探

概述:traits是一种特性萃取技术,它在Generic Programming中被广泛运用,常常被用于使不同的类型可以用于相同的操作,或者针对不同类型提供不同的实现.traits在实现过程中往往需要用到以下三种C++的基本特性:enumtypedeftemplate (partial) specialization其中:enum用于将在不同类型间变化的标示统一成一个,它在C++中常常被用于在类中

大卫的思维空间 3728

traits技术详解

STL模版库非常强调软件的复用,traits技术是采用的重要手段。traits提取不同类的共性,以便能统一处理。traits技术依靠显示模版特殊化来把代码中因类型不同而发生变化的片段拖出来,用统一的接口来包装。这个接口可以包含一个C++类所能包含的任何东西,如内嵌类型、成员函数、成员变量。作为客户的模版代码,可以通过traits模版类所公开的接口来简洁访问。 示例:一直整形数组类CI

阿里Darker 2023

traits技术

traits技术利用显示模板特殊化来把代码中因类型不同而发生变化的片段拖出来,用统一的接口来包装。traits就像是特性的提取工具,提取不同类的共同特性,以便统一处理。 下面以一个例子说明此技术 #include <iostream> #include <cstdlib> using namespace std; class CIntAry { int ar...

weixin_42067873的博客 468

Traits 技术

什么是traits,为什么人们把它认为是C++ Generic Programming的重要技术? 简洁地说,traits如此重要,是因为此项技术允许系统在编译时根据类型作一些决断,就好像在运行时根据值来做出决断一样。更进一步,此技术遵循“另增一个间接层”的谚语,解决了不少软件工程问题,traits使您能根据其产生的背景(context) 来做出抉择。这样最终的代码就变得清晰易读,容易维护。

飞鸟 981

traitsTraits技术学习笔记

traitsTraits技术初探 概述: traits是一种特性萃取技术,它在Generic Programming中被广泛运用,常常被用于使不同的类型可以用于相同的操作,或者针对不同类型提供不同的实现.traits在实现过程中往往需要用到以下三种C++的基本特性: enum typedef template (partial) specialization 其中: enum用

wei.zhou的专栏 860

STL 中traits 技术的应用

都说traits 是STL源码的钥匙,那么今天我

dalleny的专栏 1397

C++ Traits技术

原文地址:http://www.cppblog.com/woaidongmao/archive/2008/11/09/66387.html 概述: traits是一种特性萃取技术,它在Generic Programming中被广泛运用,常常被用于使不同的类型可以用于相同的操作,或者针对不同类型提供不同的实现.traits在实现过程中往往需要用到以下三种C++的基本特性:

salt11的博客 261

java traits_Traits技术初探

概述:traits是一种特性萃取技术,它在Generic Programming中被广泛运用,常常被用于使不同的类型可以用于相同的操作,或者针对不同类型提供不同的实现.traits在实现过程中往往需要用到以下三种C++的基本特性:enumtypedeftemplate(partial)specialization其中:enum用于将在不同类型间变化的标示统一成一个,它在C++中常常被用于在类中替...

weixin_39641231的博客 516

c++ traits特性萃取技术

traits,又被叫做特性萃取技术,说得简单点就是提取“被传进的对象”对应的返回类型,让同一个接口实现对应的功能。因为STL的算法和容器是分离的,两者通过迭代器链接。算法的实现并不知道自己被传进来什么。萃取器相当于在接口和实现之间加一层封装,来隐藏一些细节并协助调用合适的方法,这需要一些技巧(例如,偏特化)。最后附带一个小小的例子,应该能更好地理解 特性萃取。下面大部分来源于《STL源码剖析》,看原书能了解更多细节。...

1446
上一篇: C++中令人混淆的3个new和delete
下一篇: 重载++/--运算符
hycoldrain
博客等级 码龄20年 7粉丝 15原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值