fuzhe1989 / blogissues

0 stars 0 forks source link

2018/04/21/crtp/ #3

Open utterances-bot opened 3 years ago

utterances-bot commented 3 years ago

Curiously Recurring Template Pattern(CRTP) | Fu Zhe's Blog

CRTPC++中有一种很特别的模式,称为Curiously Recurring Template Pattern,缩写是CRTP。从它的名字看,前三个词都是关键字。Curiously,意思是奇特的。Recurring,说明它是递归的。Template,说明它与模板有关。 最常见的CRTP形式就很符合这三个关键字: 12345678template class B

http://fuzhe1989.github.io/2018/04/21/crtp/

hypheng commented 3 years ago
template <typename Derived>
class Shape_CRTP : public Shape {
public:
    virtual Shape* Clone() const {
        return new Derived(static_cast<Derived const&>(*this));
    }
};

virtual 可以不用了

fuzhe1989 commented 3 years ago
template <typename Derived>
class Shape_CRTP : public Shape {
public:
    virtual Shape* Clone() const {
        return new Derived(static_cast<Derived const&>(*this));
    }
};

virtual 可以不用了

多谢,似乎还需要加override

BowenXiao1999 commented 3 years ago

好文章

不过现在的需求是这样的,有一个工厂函数,返回一个Base,这个Derive1 和 Derive2都继承这个Base,现在相安无事

Base* FactoryCreate() {
switch (...)
case1: {
return Derive1*;

case2: {
return Derive2*;

.... // 还有很多Derive...
}
}
}

如果用CRTI,那么意味着我们的Base是一个template class,这可能会导致我们需要改写成

template <typename T>
Base<T>* FactoryCreate() {
switch (...)
case1: {
return Derive1*;

case2: {
return Derive2*;

.... // 还有很多Derive...
}
}

如果我们有另外一个类用了Base, 比如

class X {
Base* base
}

是不是也要改写成template class呢?

template <typename Derived> class X {
Base<Derived>* base
}

这需要我们在Create的时候就申明Derived类型,也就是说放弃了多态呀。感觉不是很实用。。。

fuzhe1989 commented 3 years ago

@BowenXiao1999 我感觉可以分成接口类与实现类,接口类 IBase 用于类型擦除,实现类 BaseImpl 则是这种 CRTP 风格,大概长这样:

class IBase {
public:
    virtual void func() = 0;
    virtual ~IBase() = default;
};

template <typename Derived>
class BaseImpl : public IBase {
public:
    void func() override {
        static_cast<Derived*>(this)->funcImpl();
    }
};

class Derived : public BaseImpl<Derived> {
public:
    void funcImpl() {
        // ...
    }
};
BowenXiao1999 commented 3 years ago

是的,我也觉得这个是一种办法,不过对func的调用应该还是避免不了Virtual Function Call?不知道这种和Derived直接实现IBase的纯虚函数func的实现有多少性能上的差异。

fuzhe1989 commented 3 years ago

@BowenXiao1999 如果需求就是运行期的dispatch,那这次virtual function call是免不了的。与上面方案相对应的传统方案,实际是IBase和Derived中间还有个Abstract类,负责提供具体实现的模板,其中Abstract与Derived之间还会有virtual function call。

可以参考ClickHouse的IAggregateFunction::addBatch,它的功能实际是通过IAggregateFunctionHelper提供的,后者转调用实现类的add接口,但这里因为使用了CRTP,它知道实现类的具体类型,这次add就不需要走虚函数。