【问题标题】:Injecting implementation from inherited CRTP to inherited interface class从继承的 CRTP 注入实现到继承的接口类
【发布时间】:2017-08-10 11:13:36
【问题描述】:

我想摆脱重复代码而不产生额外的 v-table 成本。

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

这里是代码。它工作正常。 (Demo)

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)

将重复的代码移动到基类(Demo):-

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-&gt;show() 会导致 2 个 v-table 查找。 (?)

我知道“过早的优化是邪恶的”,但我想知道是否可以将 v-table 成本降低到 1。(在找借口并使用这种方法之前)

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

使用 CRTP (demo)

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 新手。

【问题讨论】:

  • 解决方案 1 有什么问题?
  • @BЈовић 我相信它需要 2 个 vtable 查找:showdifferentX
  • 显然,要使解决方案 2 起作用,假设您要重用显示代码,您的 C 类必须从 B 派生。但是,解决方案 1 通常会更好,但它确实取决于实际代码和用法......
  • 第一次调用的开销更大,第二次调用的开销不大,因为“this”被加载到缓存中。应该只是间接函数调用。还是我错了?
  • @Phil1970 听起来不错....我会对此进行更多调查。谢谢。

标签: c++ interface c++14 crtp vtable


【解决方案1】:

这个解决方案怎么样?

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();  
}

【讨论】:

    【解决方案2】:

    解决方案 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!
    }
    

    【讨论】:

    • 这个问题是基类污染。在有许多派生类的情况下,其中几个具有相同的功能,您将不得不在基类中添加许多函数。
    【解决方案3】:

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

    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 );
        }
    };
    

    另请参阅“Is it always a best practice to write a function for anything that needs to repeat twice?

    【讨论】:

    • 那给我一些想法谢谢。 differentX() 是否也在 B 中声明为虚拟? different1()different2() 是怎么调用的?
    • @cppBeginner different()是基类中的虚方法吧?
    • 这个答案是否基于解决方案 2? different1different2 应该是 differentImpl 吗?像我这样的新手很难想象剩下的代码。
    • @cppBeginner 不是,是基于方案一的。可以看到,这段代码中没有CRPT。
    • 哦,我明白了。谢谢。
    猜你喜欢
    • 2018-05-28
    • 1970-01-01
    • 2014-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-18
    • 2020-10-17
    • 2016-02-12
    相关资源
    最近更新 更多