奇异的递归模板(CRTP),奇异的泛型Trick

2 篇文章 0 订阅

引言

最近碰到了如下的写法:

public interface TBase<T extends TBase<T,F>, F extends xxx> {
	xxx
}

了解过后,发现是一个Java泛型Trick1,并且在C++中也有术语CRTP2,简单的记录一下。

Java:泛型Trick

在唯一能找到的例子当中,该写法的好处是:允许我们能够在基类/接口中定义具体子类相关的方法
在一般的Interface/BaseClass中,我们定义的无外乎是:

	public void/int/String/BaseClass/Interface xxx(void/int/String/BaseClass/Interface ..);

举个例子,有一Fruit接口,有一Apple类实现,不用泛型的写法:

public interface Fruit {
	Fruit getFruit();
};
// 另一个文件...
public class Apple implements Fruit {

	@Override
	public Fruit getFruit() {
		return this;
	}
};

用泛型的写法:

public interface Fruit<F extends Fruit<F>> {
	F getFruit();
};
// 另一个文件...
public class Apple implements Fruit<Apple> {

	@Override
	public Apple getFruit() {
		return this;
	}
};

可直接返回子类/使用子类参数,有效避免了instanceof等多余操作。

C++:CRTP

奇异递归模板与该Trick有异曲同工之妙,在Wiki2上解释得非常详细,这里做个简化的解释。
基本格式:

template <class T>
class Base
{
	// ...
};
class Derived : public Base<Derived>
{
    // ...
};

在Wiki中,CRTP的用途十分多样,可以类比到Java进行尝试(todo)

静态多态

template <class T> 
struct Base {
    static void f() {
        T::subf();
    }
};

struct Derived : Base<Derived> {
    static void subf();
};

// ...
void test(Base& base) {
	base.f();
}

基类的方法f()直接调用子类的静态方法subf()

对象计数器

template <typename T>
struct Counter {
    static int objects_created;
    static int objects_alive;

    Counter() {
        ++objects_created;
        ++objects_alive;
    }
    
    Counter(const counter&) {
        ++objects_created;
        ++objects_alive;
    }
protected:
    ~Counter() {
        --objects_alive;
    }
};
template <typename T> int Counter<T>::objects_created( 0 );
template <typename T> int Counter<T>::objects_alive( 0 );

class X : Counter<X> {
};

class Y : Counter<Y> {
}

类X和类Y分别有各自不同的对象计数器,很容易理解

多态链

多态链代码过多,其作用和上述Java泛型Trick如出一辙,允许基类定义具体子类相关的方法,最终实现基类子类链式调用的效果:

	base.basef().concretef();

多态复制

模板类继承接口(抽象类),通过重写接口的复制方法,实现子类的复制,可拓展到其他通用方法。
例如:

// 接口
class AbstractBase {
public:
    virtual ~AbstractBase () = default;
    virtual std::unique_ptr<AbstractBase> clone() const = 0;
};

// 模板基类实现AbstractBase,重写clone()方法
template <typename T>
class Base : public AbstractBase {
public:
    std::unique_ptr<AbstractBase> clone() const override {
        return std::make_unique<T>(static_cast<T const&>(*this));
    }

protected:
   // We make clear Shape class needs to be inherited
   Base() = default;
   Base(const Base&) = default;
};

class C1 : public Base<C1>{};
class C2 : public Base<C2>{};

只需使用

	basePtr->clone();

即可任意拷贝C1、C2的实例


  1. 见网站之More tricks with type parameters小节 ↩︎

  2. Curiously recurring template pattern,奇异的递归模板,见 Wiki ↩︎ ↩︎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值