将实现从继承的 CRTP 注入到继承的接口类

Injecting implementation from inherited CRTP to inherited interface class

本文关键字:继承 接口 注入 CRTP 实现      更新时间:2023-10-16

我想在不产生额外对等表成本的情况下摆脱重复的代码。

  • B是一个接口类:它不能是虚拟的
  • D1D2是B的具体类。
    • 它们的某些部分是相同的:在mcve中显示为std::cout<<"same"
    • 有些部分是不同的:显示为different1()different2()

这是代码。 它工作正常。 (演示(

class B{//can't be template
public: virtual void show()=0;
};
class D1 : public B{
public: bool different1(){return true;}
public: virtual void show(){ 
if( different1() )
std::cout<<"same";   //duplicate
}
};
class D2 : public B{
public: bool different2(){return true;}
public: virtual void show(){ 
if( different2() )
std::cout<<"same";   //duplicate
}
};
int main(){
D1 d;
B* b=&d;
b->show();  //1 v-table look up : OK!
}

我糟糕的解决方案 (1/2(

将重复的代码移动到基类(演示(:-

class B{//can't be template
public: virtual void show(){
if( differentX() )
std::cout<<"same";   //duplicate
}
public: virtual bool differentX() = 0;
};
class D1 : public B{
public: bool differentX(){return true;}
};
class D2 : public B{
public: bool differentX(){return true;}
};

问题是b->show()会产生 2 次对表查找。 (?)

我知道"过早优化是邪恶的",但我想听听是否有可能将逆向表成本降低到 1。(在找借口并使用这种方法之前(

我糟糕的解决方案 (2/2(

使用 CRTP(演示(

class B{//can't be template
public: virtual void show()=0;
};
template<class T>class C{  //
public: bool differentX(){
return static_cast<T*>(this)->differentImpl()   ;
}
public: void show(){
differentX();
std::cout<<"same"; 
}
};
class D1 : public B, public C<D1>{
public: bool differentImpl(){return true;}
};
class D2 : public B, public C<D2>{
public: bool differentImpl(){return true;}
};

但是,它不再兼容,因为D1不再是一个具体的类。

如何解决? 我是CRTP的新手。

这是如何解决的?

C 实现了 B 的 show 方法,并提供了 show 的通用实现,该实现依赖于模板参数 T 提供不同的实现。

D1 和 D2 继承自 C,并将它们自己作为模板参数。

http://coliru.stacked-crooked.com/a/34e8a727e81e19f7

#include <iostream>
class B {//can't be template
public: virtual void show() = 0;
};
template<class T>
class C : public B {
public:
void show() override {
if (Impl().different()) {
std::cout << "same"; 
}
}
private:
T& Impl() {
return *static_cast<T*>(this);
}
};
class D1 : public C<D1> {
public: bool different() { return true; }
};
class D2 : public C<D2> {
public: bool different() { return true; }
};
int main() {
D1 d;
B* b = &d;
b->show();  
}

解决方案 3 :将重复的代码放在函数中。像这样:

class B{//can't be template
public: virtual void show()=0;
public: void same(){
std::cout<<"same";   //no duplicate
}
};
class D1 : public B{
public: bool different1(){return true;}
public: virtual void show(){ 
if( different1() )
same();
}
};
class D2 : public B{
public: bool different2(){return true;}
public: virtual void show(){ 
if( different2() )
same();
}
};
int main(){
D1 d;
B* b=&d;
b->show();  //1 v-table look up : OK!
}

正如我在评论中所说,您可以创建一个具有通用功能的函数:

void ShowFuncX( B& b )
{
if ( b.differentX() )
{
std::cout<<"same";
}
}

然后在派生中:

class D1 : public B{
public: bool different1(){return true;}
public: virtual void show(){ 
ShowFuncX( *this );
}
};
class D2 : public B{
public: bool different2(){return true;}
public: virtual void show(){ 
ShowFuncX( *this );
}
};

另请参阅"为需要重复两次的任何内容编写函数是否始终是最佳做法?