c++ template(9)trait和Policy

本文深入探讨了C++中的模板参数和特质(traits)的概念,通过实例展示了如何利用特质来配置通用计算中的具体元素类型,以及如何通过特质参数化来改变模板参数的默认值,同时介绍了策略(policy)的概念来实现算法的选择。

 

先看一个Demo:

累加序列:

template <typename T> 
inline 
    T accum (T const* beg, T const* end) 
{ 
    T total = T();  // assume T() actually creates a zero value 
    while (beg != end) { 
        total += *beg; 
        ++beg; 
    } 
    return total; 
} 

使用示例:

int main() 
{
    // create array of 5 integer values 
    int num[]={1,2,3,4,5}; 

    // print average value 
    std::cout << "the average value of the integer values is " 
        << accum(&num[0], &num[5]) / 5 
        << '\n'; 

    // create array of character values 
    char name[] = "templates"; 
    int length = sizeof(name)-1; 

    // (try to) print average character value 
    std::cout << "the average value of the characters in \"" 
        << name << "\" is " 
        << accum(&name[0], &name[length]) / length 
        << '\n'; 

    return 1;

}

结果:

image

显然char的类型有问题,变量total 应该转化为int

修改方案则是新加一个模版参数:

template <typename T,typename V=int> 
inline 
    V accum (T const* beg, T const* end) 
{ 
    V total = V();  // assume T() actually creates a zero value 
    while (beg != end) { 
        total += *beg; 
        ++beg; 
    } 
    return total; 
} 

同时添加了使用成本

accum<char,int>(&name[0], &name[length])

模版的trait力量:将动态模版参数定义到外部结构中

template<typename T> 
class AccumulationTraits; 

template<> 
class AccumulationTraits<char> { 
public: 
    typedef int AccT; 
}; 

template<> 
class AccumulationTraits<short> { 
public: 
    typedef int AccT; 
}; 

template<> 
class AccumulationTraits<int> { 
public: 
    typedef long AccT; 
}; 

template<> 
class AccumulationTraits<unsigned int> { 
public: 
    typedef unsigned long AccT; 
}; 

template<> 
class AccumulationTraits<float> { 
public: 
    typedef double AccT; 
}; 

template <typename T> 
inline 
    typename AccumulationTraits<T>::AccT accum (T const* beg, 
    T const* end) 
{ 
    // return type is traits of the element type 
    typedef typename AccumulationTraits<T>::AccT AccT; 

    AccT total = AccT();  // assume T() actually creates a zero value 
    while (beg != end) { 
        total += *beg; 
        ++beg; 
    } 
    return total; 
} 

现在可以保持调用方式保持不变,通过trait来更改类型参数

accum(&name[0], &name[length])

现在结果是正确的

image

trait的默认值

trait类型的初始值用构造函数初始化可能会有问题,所以可以再次用trait来定义默认值

默认值方式:1.静态变量(只能整型数值和枚举),2.静态方法(推荐)

template<> 
class AccumulationTraits<char> { 
public: 
    typedef int AccT; 
    //static AccT const zero = 0; 
    static AccT zero() { 
        return 0; 
    } 

}; 

更改后的初始化方式:

template <typename T> 
inline 
    typename AccumulationTraits<T>::AccT accum (T const* beg, 
    T const* end) 
{ 
    // return type is traits of the element type 
    typedef typename AccumulationTraits<T>::AccT AccT; 

    AccT total = AccumulationTraits<T>::zero();
    while (beg != end) { 
        total += *beg; 
        ++beg; 
    } 
    return total; 
} 

This is the key of the traits concept: Traits provide an avenue to configure concrete elements (mostly types) for generic computations.

trait参数化

有时候我们想改变通过trait本身的默认模版参数,这样就需要对trait本事做一个参数封装

封装一个默认的模版参数

template <typename T, 
    typename AT = AccumulationTraits<T> > 
class Accum { 
public: 
    static typename AT::AccT accum (T const* beg, T const* end) { 
        typename AT::AccT total = AT::zero(); 
        while (beg != end) { 
            total += *beg; 
            ++beg; 
        } 
        return total; 
    } 
}; 

1

现在之前的方法应该如下示例:

template <typename T> 
inline 
    typename AccumulationTraits<T>::AccT accum (T const* beg, 
    T const* end) 
{ 
    return Accum<T>::accum(beg, end); 
} 

trait参数化

template <typename Traits, typename T> 
inline 
    typename Traits::AccT accum (T const* beg, T const* end) 
{ 
    return Accum<T, Traits>::accum(beg, end); 
} 

 

Policy

Policy注重算法,如下示例:Policy是一个静态类,通过动态编译来计算

template <typename T, 
    typename Policy = SumPolicy, 
    typename Traits = AccumulationTraits<T> > 
class Accum { 
public: 
    typedef typename Traits::AccT AccT; 
    static AccT accum (T const* beg, T const* end) { 
        AccT total = Traits::zero(); 
        while (beg != end) { 
            Policy::accumulate(total, *beg); 
            ++beg; 
        } 
        return total; 
    } 
}; 

不同Policy算法

class SumPolicy { 
public: 
    template<typename T1, typename T2> 
    static void accumulate (T1& total, T const & value) { 
        total += value; 
    } 
}; 

class MultPolicy { 
public: 
    template<typename T1, typename T2> 
    static void accumulate (T1& total, T const& value) { 
        total *= value; 
    } 
}; 

使用示例:

int main() 
{ 
    // create array of 5 integer values 
    int num[]={1,2,3,4,5}; 

    // print product of all values 
    std::cout << "the product of the integer values is " 
        << Accum<int,MultPolicy>::accum(&num[0], &num[5]) 
        << '\n'; 
} 

转载于:https://www.cnblogs.com/Clingingboy/archive/2013/03/29/2988604.html

<think>好的,我需要回答用户关于PHP中traitinterface的区别及使用场景的问题。首先,我需要回忆一下PHP中的traitinterface的基本概念。 根据用户提供的引用内容,interface使用关键字interface定义,类通过implements实现接口,必须实现所有方法。例如,User接口有两个方法,VipUser类实现了它们。接口主要用于定义规范,确保实现类有特定的方法。 而trait则是用关键字trait定义,用于代码复用。因为PHP是单继承,trait允许在不继承另一个类的情况下复用方法。例如,引用[2][3]提到trait解决单继承限制,可以在不同类中复用方法。比如,如果有多个类需要同样的方法,可以用trait来包含这些方法,避免重复代码。 接下来,我需要比较它们的区别。接口是定义方法签名,要求实现类必须实现这些方法,属于一种契约。而trait是提供具体的方法实现,直接注入到类中,不需要再实现。接口关注于“能做什么”,而trait关注于“有什么功能”。 使用场景方面,接口适合定义统一的行为规范,比如不同的用户类型都需要有getDiscount方法,那么用接口确保一致性。而trait适合多个不相关的类需要共享相同的方法,比如日志功能或工具方法,可以在多个类中使用同一个trait,避免代码重复。 还要注意接口可以实现多个,但trait可以组合多个,但使用时可能会有冲突,需要解决冲突。比如引用[3]提到trait组合时可能出现方法名冲突,需要用insteadof或as来调整。 需要确保回答结构清晰,分点说明区别使用场景,并给出示例,符合用户提供的引用内容。同时检查是否有遗漏点,比如finalabstract的区别,但用户问题只关注traitinterface,所以不需要涉及其他概念。 最后,生成相关问题,可能需要包括trait与抽象类的区别,如何解决trait冲突,接口的应用场景等。确保问题相关且有参考价值。</think>### PHP中Trait与Interface的区别及使用场景分析 #### 一、核心区别对比 1. **定义方式与语法** - **Interface**:通过`interface`关键字定义,仅包含方法签名(无具体实现)。例如: ```php interface Logger { public function log($message); } ``` - **Trait**:通过`trait`关键字定义,包含具体方法实现。例如: ```php trait FileLogger { public function log($message) { file_put_contents(&#39;log.txt&#39;, $message, FILE_APPEND); } } ``` 2. **实现方式** - **Interface**:类必须通过`implements`显式实现接口,并强制定义所有方法[^1]。 ```php class UserLogger implements Logger { public function log($message) { // 必须实现 } } ``` - **Trait**:通过`use`关键字将方法注入类中,无需重复编写代码[^2]。 ```php class OrderService { use FileLogger; // 直接获得log方法 } ``` 3. **设计目标** - **Interface**:定义**行为规范**,强调"能做什么"(如用户类型必须支持折扣计算)[^1]。 - **Trait**:实现**代码复用**,解决单继承限制,提供"现成功能"(如日志、缓存等工具方法)[^3]。 #### 二、使用场景对比 | 特性 | Interface | Trait | |---------------------|-------------------------------------|--------------------------------| | **多态支持** | ✅ 通过类型约束实现 | ❌ 无法单独类型约束 | | **代码复用** | ❌ 仅定义规范 | ✅ 直接注入实现 | | **多继承模拟** | ✅ 类可实现多个接口 | ✅ 类可组合多个trait | | **版本兼容性** | ✅ 所有PHP版本支持 | ✅ PHP 5.4+ 支持 | **典型场景示例:** 1. **Interface适用场景** 当需要统一不同类的方法调用标准时,例如支付网关集成: ```php interface PaymentGateway { public function charge($amount); } class StripeGateway implements PaymentGateway { public function charge($amount) { /* Stripe逻辑 */ } } class PayPalGateway implements PaymentGateway { public function charge($amount) { /* PayPal逻辑 */ } } ``` 2. **Trait适用场景** 当多个无关类需要共享相同功能时,例如添加日志功能: ```php trait RequestTracer { protected function logRequest() { // 记录请求信息的通用实现 } } class ApiController { use RequestTracer; public function handle() { $this->logRequest(); } } class BatchJob { use RequestTracer; public function run() { $this->logRequest(); } } ``` #### 三、组合使用技巧 二者可协同工作,例如通过接口定义规范,用trait提供默认实现: ```php interface Cacheable { public function getCacheKey(); } trait RedisCache { public function cache($data) { $key = $this->getCacheKey(); // 依赖接口方法 Redis::set($key, $data); } } class ProductRepository implements Cacheable { use RedisCache; public function getCacheKey() { return &#39;product_&#39; . $this->id; } } ``` 此模式强制实现类定义`getCacheKey()`,同时复用缓存逻辑[^3]。 #### 四、冲突处理 当多个trait包含同名方法时,需使用`insteadof``as`解决冲突: ```php trait FormatterA { public function format() { /* A逻辑 */ } } trait FormatterB { public function format() { /* B逻辑 */ } } class Report { use FormatterA, FormatterB { FormatterA::format insteadof FormatterB; FormatterB::format as formatRaw; } } ``` 此时`format()`使用FormatterA的实现,FormatterB的实现可通过`formatRaw()`调用[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值