简体   繁体   中英

C++ Specialize Inherited Template in Derived Class

I'm trying to write a program where derived classes of ServiceWorker such as Firefighter can "confront" objects derived from class Incident (Fire, Injury, Robbery). The confrontation would return a boolean value of whether or not it succeeded. For example, a firefighter confronting a fire would be successful, but a firefighter confronting a robbery would not. ServiceWorker would track the number of confrontations and successful confrontations as well as have a confront method that will determine if the confrontation was successful.

I want to use traits to store what each derived class of ServiceWorker can confront in its class definition. However, in ServiceWorker the confront method creates the traits class, but I want to specialize it in its derived classes so each derived class only knows what it can confront.

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;
    };
};

EDIT

So I managed to figure it out. I got rid of templates and made confront virtual, so making a derived class of ServiceWorker would only require redefining the virtual function and using ServiceWorker::confront. I also added confront_success() and calc_ratio() to make adding new ServiceWorkers easier. I made Firefighter::confront() take FirefighterIncident, which inherited from Incident. That way, adding new incidents for Firefighter would be easy since they'd only have to inherit from FirefigherIncident.

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(); }
};

In my view, things get cleaner if you take advantage of std::true_type and std::false_type . In the base class,

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

and in the derived class,

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

Notice that I redefined can_confront in the derived class (through inheritance), so that I can specialize it without specializing ServiceWorker::can_confront . To do this, simply add

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

outside the class definition. The confront template method will then work once the if statement is replaced with

if(can_confront<T>::value)

However , remember that you're still left with the problem that confront always uses ServiceWorker::can_confront<T> , even when you call it through a Firefighter object. An easy fix would be to copy the definition of confront into each derived class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM