Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2018/04/21/crtp/ #3

Open
utterances-bot opened this issue Apr 27, 2021 · 6 comments
Open

2018/04/21/crtp/ #3

utterances-bot opened this issue Apr 27, 2021 · 6 comments

Comments

@utterances-bot
Copy link

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/

Copy link

hypheng commented Apr 27, 2021

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

virtual 可以不用了

@fuzhe1989
Copy link
Owner

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

virtual 可以不用了

多谢,似乎还需要加override

Copy link

好文章

不过现在的需求是这样的,有一个工厂函数,返回一个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
Copy link
Owner

@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() {
        // ...
    }
};

Copy link

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

@fuzhe1989
Copy link
Owner

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants