简体   繁体   English

如何将模板派生类的方法提取到非模板基类中

[英]how to extract template derived class's method into non-template base class

I want using polymorphism in C++, I am try to extract method shows in all derived class into base class. 我想在C ++中使用多态,我尝试将所有派生类中显示的方法提取到基类中。

For example: 例如:

I have two class, HouseA and HouseB , they are template class. 我有两个类, HouseAHouseB ,它们是模板类。 And they are derived from base class BaseHouse . 它们是从基类BaseHouse

class BaseHouse
{
public:
    //other thing
private:
};

template <typename Type>
class HouseA : public BaseHouse
{
public:
    HouseA(Type object_input) : object(object_input)
    {
    }
    // other thing about HouseA
    Type &getObject()
    {
        std::cout << "this is House A" << std::endl;
        return object;
    }

private:
    Type object;
};

template <typename Type>
class HouseB : public BaseHouse
{
public:
    HouseB(Type object_input) : object(object_input)
    {
    }
    // other thing about HouseB
    Type &getObject()
    {
        std::cout << "this is House B" << std::endl;
        return object;
    }

private:
    Type object;
};

Bacause of polymorphism, we using base class's pointer to access derivated class object. 由于多态性,我们使用基类的指针来访问派生类对象。 When I need to call method defined in derivated class, I am always transfer base class pointer into derivated class pointer: 当我需要调用派生类中定义的方法时,我总是将基类指针转换为派生类指针:

int main()
{
    HouseA<int> house_a(5);
    int x = house_a.getObject();

    BaseHouse *base_ptr = &house_a;

    // suppose after some complicate calculate calculation
    // we only have the base class pointer can access derivated class object

    HouseA<int> *ptr_a = (HouseA<int> *)base_ptr; //transfer base class pointer into derivated class pointer
    ptr_a->getObject();
    return 0;
}

But the derived class HouseA and HouseB both have the method getObject . 但是派生类HouseAHouseB都具有getObject方法。

So I want to extract template derived class's method into non-template base class. 所以我想将模板派生类的方法提取到非模板基类中。

For some reason, we suppose that the base class BaseHouse can not be template class. 由于某种原因,我们假设基类BaseHouse不能为模板类。

Is there any way I can do that? 有什么办法可以做到吗?

Thanks in advance. 提前致谢。

If the signature of the derived member depends on the template arguments (as your getObject does on Type) the member cannot be extracted into a non-template base. 如果派生成员的签名取决于模板参数(如getObject在Type上所做的那样),则无法将成员提取到非模板库中。 At least not without removing the ability of the member's signature to vary based on template arguments. 至少没有消除成员签名根据模板参数变化的能力。

Maybe not exactly a classical Visitor, but... 也许不完全是一个经典的访客,但是...

Okay, the basic idea is we have to somehow capture and encapsulate templated processing into a single entity ready-to-use in a run-time polymorphic construct. 好的,基本思想是我们必须以某种方式捕获模板化的处理并将其封装到一个可在运行时多态构造中使用的单个实体中。

Let's start with a simple class hierarchy: 让我们从一个简单的类层次结构开始:

struct Consumer;

struct Base {
    virtual void giveObject(Consumer const &) const = 0;
    virtual ~Base() = default;
};

struct Derived1: Base {
    Derived1(int x): x(x) {}
    void giveObject(Consumer const &c) const override {
        c(x);
    }
private:
    int x;
};

struct Derived2: Base {
    Derived2(double y): y(y) {}
    void giveObject(Consumer const &c) const override {
        c(y);
    }
private:
    double y;
};

So far, it is very simple: the Base class has a pure virtual method that accepts an object of type Consumer and a concrete implementation of this method is expected to expose to Consumer the relevant part of the internal state of its particular implementor (which is a subtype of Base ). 到目前为止,这非常简单: Base类具有一个纯虚拟方法,该方法接受类型为Consumer的对象,并且期望该方法的具体实现向Consumer公开其特定实现者内部状态的相关部分(即Base的子类型)。 In other words, we have taken that 'virtual template' idiom and hid it inside the Consumer . 换句话说,我们采用了“虚拟模板”惯用法并将其隐藏在Consumer Ok, what could it possibly be? 好吧,那可能是什么?

First option, if you know in advance at compile-time (at source code-time, more exactly) what it could possibly do, ie there's only one algorithm of consumption per each object type, and the set of types is fixed, it is quite straightforward: 第一种选择是,如果您提前在编译时(在源代码时,更准确地知道)知道它可能做什么,即每种对象类型只有一个消耗算法,并且类型集是固定的,那就是很简单:

struct Consumer {
    void consume(int x) const { std::cout << x << " is an int.\n"; }
    void consume(double y) const { std::cout << y << " is a double.\n"; }
    template<typename T> void consume(T t) const {
        std::cout << "Default implementation called for an unknown type.\n";
    }
};

etc. 等等

More elaborate implementation would allow run-time construction of a templated entity. 更详尽的实现将允许在运行时构造模板化实体。 How is that even possible? 这怎么可能呢?

Alexandrescu in his "Modern C++ Design" uses typeid to store particular type handlers in a single data structure. Alexandrescu在他的“现代C ++设计”中使用typeid将特定的类型处理程序存储在单个数据结构中。 In a brief, this could be something like: 简而言之,可能是这样的:

struct Handler {
    virtual ~Handler() = default; // now it's an empty polymorphic base
};

template<typename T> struct RealHandler: Handler {
    RealHandler(std::function<void(T)> f): f(std::move(f)) {}
    void handle(T x) {
        f(x);
    }
private:
    std::function<void(T)> f;
};

#include <map>
#include <type_info>
#include <functional>

struct Consumer {
    template<typename T> void consume(T t) const {
        auto f{knownHandlers.find(typeid(t))};
        if(f != knownHandlers.end()) {
            RealHandler<T> const &rh{
                dynamic_cast<RealHandler<T> const &>(*f->second)};
            rh.handle(t);
        }
        else {
            // default implementation for unregistered types here
        }
    }
    template<typename T> Consumer &register(std::function<void(T)> f) {
        knownHandlers[typeid(T)] = std::make_unique<RealHandler<T>>(std::move(f));
    }
private:
    std::map<std::type_info, std::unique_ptr<Handler>> knownHandlers;
};

Haven't actually tested it, as I don't like typeids and other RTTI much. 还没有实际测试过,因为我不太喜欢typeid和其他RTTI。 What I have quickly tested is another solution that requires neither maps nor typeinfo to store handlers in a templated manner. 我快速测试过的另一种解决方案既不需要映射也不需要typeinfo来以模板化方式存储处理程序。 Still it uses a small trick, like how can we possibly pass, keep and retrieve information of an arbitrary type with the same call. 它仍然使用了一个小技巧,例如我们如何通过相同的调用传递,保留和检索任意类型的信息。

struct Consumer {
    Consumer() {}
    template<typename T> void consume(T t) const {
        auto f{setSlot<T>()};
        if(f) f(t);
        else {
            // default implementation for an unset slot
            std::cout << t / 2 << '\n';
        }
    }
    template<typename T>
    std::function<void(T)> &setSlot(
            std::function<void(T)> f = std::function<void(T)>{}) const
    {
        static std::function<void(T)> slot;
        if(f) { // setter
            slot = std::move(f);
        }
        return slot;
    }
};

Here, setSlot() is used to store a handler for a particular type: when called with a non-empty argument, it stores that argument; 在这里, setSlot()用于存储特定类型的处理程序:当用非空参数调用时,它将存储该参数; and then returns its currently kept value. 然后返回其当前保留的值。 With Consumer so defined, the class hierarchy from above works as: 在定义了Consumer ,上面的类层次结构将用作:

int main() {
    Consumer c;
    c.setSlot<int>([](int x){ std::cout << x << " is an int!\n"; });
    Base const &b1{Derived1{42}};
    Base const &b2{Derived2{3.14}};
    b1.giveObject(c);
    b2.giveObject(c);
}

Output: 输出:

42 is an int!
1.57

In the first line we see a message printed by a custom int handler; 在第一行中,我们看到了一个由自定义int处理程序打印的消息; in the second line, a default message is printed for the double type, as no custom handler for double was installed. 在第二行中,将为double类型打印默认消息,因为未安装double自定义处理程序。

One obvious drawback of this implementation is that handlers are stored in static variables thus all Consumer s share the same handlers for all types, so Consumer here is actually a monostate. 此实现的一个明显的缺点是处理程序存储在static变量中,因此所有Consumer都对所有类型共享相同的处理程序,因此此处的Consumer实际上是单态的。 At least, you can change implementations for types at run-time, unlike if you had fixed Consumers of the very first approach. 至少,您可以在运行时更改类型的实现,这与您是否拥有固定的第一种方法的Consumers不同。 The maps-of-typeids approach from above shouldn't have this drawback, in exchange for some performance cost. 上面提到的typed-of-typeids方法不应具有此缺点,以换取一些性能成本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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