C ++在派生类中专门化继承模板

Posted

技术标签:

【中文标题】C ++在派生类中专门化继承模板【英文标题】:C++ Specialize Inherited Template in Derived Class 【发布时间】:2016-04-06 22:26:27 【问题描述】:

我正在尝试编写一个程序,其中 ServiceWorker 的派生类(例如 Firefighter)可以“对抗”派生自类 Incident(火灾、伤害、抢劫)的对象。对抗将返回是否成功的布尔值。例如,面对火灾的消防员会成功,但面对抢劫的消防员不会成功。 ServiceWorker 将跟踪对抗和成功对抗的数量,并有一个对抗方法来确定对抗是否成功。

我想使用特征来存储 ServiceWorker 的每个派生类在其类定义中可以面对的内容。但是,在 ServiceWorker 中,面对方法创建了特征类,但我想将其专门用于其派生类,因此每个派生类只知道它可以面对什么。

class Incident  ;
class Fire : Incident  ;
class Robbery : Incident  ;
class Injury : Incident  ;

class ServiceWorker 
public:
    ServiceWorker() : ratio(0), num_success(0), num_confronts(0) 

    

    template <typename U>
    struct can_confront 
        static const bool result = false;
    ;

    double successRatio()  
    if(ratio)
            return ratio;
        else 
            throw;
    
    template <typename T>
    void confront(T incident) 
        if(can_confront<T>::result)
            num_success++;
        num_confronts++;

        ratio = num_success / num_confronts;
    
protected:
    double ratio;
    unsigned int num_success;
    unsigned int num_confronts;
;

class Firefighter : public ServiceWorker 
public:
    template <>
    struct can_confront<Fire> 
        static const bool result = true;
    ;
;

编辑

所以我设法弄明白了。我去掉了模板,把 face 变成了虚拟的,所以制作一个 ServiceWorker 的派生类只需要重新定义虚拟函数并使用 ServiceWorker::confront。我还添加了 faces_success() 和 calc_ratio() 以更轻松地添加新的 ServiceWorker。我让 Firefighter::confront() 采用 FirefighterIncident,它继承自 Incident。这样,为 Firefighter 添加新事件会很容易,因为它们只需要从 FirefigerIncident 继承即可。

class Incident ;
class FirefighterIncident : public Incident ;
class Fire : public FirefighterIncident ;
class RescueCat : public FirefighterIncident ;

class ServiceWorker 
public:
    ServiceWorker() : ratio(0), num_success(0), num_confronts(0) 

    

    double successRatio()  
        if(num_confronts != 0)
            return ratio;
        else 
            std::cout << "Can't divide by zero in ratio!" << std::endl;
            throw;
        
    

    virtual void confront(const Incident&) 
        num_confronts++;
        calc_ratio();
    
protected:
    double ratio;
    unsigned int num_success;
    unsigned int num_confronts;

    void calc_ratio() 
        ratio = num_success / (double) num_confronts;
    

    void confront_success() 
        num_success++;
        num_confronts++;
        calc_ratio();
    
;

class Firefighter : public ServiceWorker 
public:
    using ServiceWorker::confront;
    virtual bool confront(const FirefighterIncident&)  confront_success(); 
;

【问题讨论】:

不,您不能这样做,您可以在派生类中更改的唯一内容是虚函数实现及其返回类型(协变)。由于confront 不是虚拟的,所以我不知道派生类的存在。 您需要双重调度才能拥有void confront(ServiceWorker&amp;, const Incident&amp;) @Jarod42 " to have double dispatch" 如果需要运行时多态性,即。 【参考方案1】:

在我看来,如果您利用std::true_typestd::false_type,事情会变得更清晰。在基类中,

class ServiceWorker 
public:
template <typename U>
struct can_confront: std::false_type  ;
...

在派生类中,

class Firefighter: public ServiceWorker 
public:
template<typename U>
struct can_confront: ServiceWorker::template can_confront<U>  ;
...

请注意,我在派生类中重新定义了can_confront(通过继承),这样我就可以在不专门化ServiceWorker::can_confront 的情况下专门化它。为此,只需添加

template<>
struct Firefighter::can_confront<Fire>: std::true_type  ;

在类定义之外。一旦if 语句被替换为

confront 模板方法就会起作用
if(can_confront<T>::value)

然而,请记住,您仍然会遇到confront 始终使用ServiceWorker::can_confront&lt;T&gt; 的问题,即使您通过Firefighter 对象调用它也是如此。一个简单的解决方法是将confront 的定义复制到每个派生类中。

【讨论】:

这些可能是不错的改进,但与问题的主旨无关。将模板复制到派生的 cassess 不会有任何效果。 @n.m.除了工作代码(OP 的代码无法编译)和他正在寻找的专业化程序之外的任何东西。在我的理解中,问题的要点正是针对多个派生类的can_confront 的专业化。多态性是不可能的,因为confront 不能成为虚拟的,但它可以实现,例如通过重载,如果真的想要的话。 是的,最初,我使用多态性使其工作,并且每个事件都有一个数字代码。当事件传递给 ServiceWorker 时,在面对()中检查了数字代码。我认为这确实是一种草率的技术,但我认为我可以使用特征类来改进它。这样做的目的是保持相同的多态性和继承能力,同时消除对数字代码的需求。

以上是关于C ++在派生类中专门化继承模板的主要内容,如果未能解决你的问题,请参考以下文章

c ++继承:基类中的2次重载运算符+在派生类中无法正常工作

c++继承是如何工作的?

C++_练习—继承_公有继承

C++ 继承:我必须在派生类中重复父属性吗?

[c++]第五章概念题 | 继承

5继承与派生3-类型兼容规则