使用模板的C ++静态分派 [英] C++ static dispatch using templates

查看:80
本文介绍了使用模板的C ++静态分派的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一些C ++代码看起来像这样:

  class Base {
virtual void dummy = 0;
//这是生成一个vtable,但是注意没有虚拟f()
};

class A:public Base {
public:
void f(){/ * ... * /};
void dummy(){};
}

class B:public Base {
public:
void f(){/ *不同于A * /}的实现;
void dummy(){};
}

模板<类T1,类T2,类T3>
void doStuff(T1& x,T2& y,T3& z){
for(i = 1; i< 100000; ++ i){
x.f
y.f();
z.f();
}
}

这里的意图是避免虚函数调用 f(),以便允许编译器优化。 (这显然是我实际代码的简化版本。有关我的用例的详细信息,请参阅 doStuff

的类型工作正常,则可以正常工作。 / code>的参数在运行时是已知的,但如果它们不是,则它失败:

  int main(){
Base * x = new A();
Base * y = new B();
Base * z = new A();

doStuff(* x,* y,* z);
// oops - 这实例化为doStuff(Base& Base,& Base,&)
//并且没有Base :: f()。 $ lt;
}

/stackoverflow.com/questions/22138009/avoiding-virtual-function-calls-in-numerical-c/22138332#22138332>这个答案)看来我必须显式地构造一个静态调度函数:

  void doStuff(Base& x,Base& y,Base& z){
A * a_x = dynamic_cast< A *>(& x);
B * b_x = dynamic_cast< B *>(& x);
A * a_y = dynamic_cast< A *>(& y);
B * b_y = dynamic_cast< B *>(& y);
A * a_z = dynamic_cast< A *>(& z);
B * b_z = dynamic_cast< B *>(& z);
if(a_x& a_y& a_z){
doStuff(* a_x,& a_y,& a_z);
} else if(a_x& a_y&& b_z){
doStuff(* a_x,& a_y,& b_z);
}
// ...对于A和B的所有八种组合,依此类推。
}

但这是一些非常烦人的重复代码,如果我有几个函数沿着 doStuff 的行,它会很快变得难以管理,特别是如果任何一个有四个或更多的参数。



所以我的问题是,有没有办法避免这个重复的代码?看起来像一些更聪明的使用模板应该能够消除它,但我不能很清楚如何做。

解决方案

你的问题的标题:static dispatch - 不幸的是你的问题需要在运行时找到类型...你想从一些基类指针识别实际的类型。



使用 dynamic_cast 是一个黑客,dynamic_cast的用法通常意味着坏OO设计。 BTW,我的打赌是,使用 dynamic_cast dynamic_cast 可以很慢!),然后静态函数调用慢于简单地使 f() virtual并把它放到你的基类并以这种方式调用。



当然你的情况是有点更特殊,你想为所有8个案件执行别的东西,但这是一块污垢工作,你将无法解决优雅与简单的代码在C + +在我看来。有可能建立一个不易出错/容易扩展的解决方案,或者一种在性能上更好的解决方案,但是它们都不会是短暂的和/或优雅的。你当前的 doStuff()实现是一个不太容易出错的防弹解决方案,另一个丑陋的解决方案刚刚来到我的想法,即使对于很多派生类和类组合(自己的类型枚举与自己的gettype +开关),但这是丑陋的地狱。



总结:这个问题在C ++没有很好的解决方案。你必须重新思考你的设计,或者你必须生活丑陋的代码,当它涉及到无法在C ++中优雅地解决的问题:例如在序列化的情况下,你经常发现丑陋的代码,像这样...


Suppose I have some C++ code that looks like this:

class Base {
    virtual void dummy() = 0;
    // this is to generate a vtable, but note there is no virtual f()
};

class A : public Base {
public:
    void f() { /* ... */ };
    void dummy() {};
}

class B : public Base {
public:
    void f() { /* different implementation from A */ };
    void dummy() {};
}

template<class T1, class T2, class T3>
void doStuff(T1 &x, T2 &y, T3 &z) {
    for (i=1; i<100000; ++i) {
        x.f();
        y.f();
        z.f();
    }
}

The intention here is to avoid virtual function calls to f() during that inner loop, in order to allow compiler optimisations. (This is obviously a simplified version of my actual code. For details of my use case, see this more specific question).

This works fine if the types of doStuff's arguments are known at run-time, but if they're not then it fails:

int main() {
    Base *x = new A();
    Base *y = new B();
    Base *z = new A();

    doStuff(*x, *y, *z); 
    // oops - this instantiates to doStuff(Base &, Base &, Base &)
    // and there's no Base::f().
}

to get around this (as suggested by this answer) it seems I have to construct a static dispatch function explicitly:

void doStuff(Base &x, Base &y, Base &z) {
    A *a_x = dynamic_cast<A*>(&x);
    B *b_x = dynamic_cast<B*>(&x);
    A *a_y = dynamic_cast<A*>(&y);
    B *b_y = dynamic_cast<B*>(&y);
    A *a_z = dynamic_cast<A*>(&z);
    B *b_z = dynamic_cast<B*>(&z);
    if (a_x && a_y && a_z) {
        doStuff(*a_x, &a_y, &a_z);
    } else if (a_x && a_y && b_z) {
        doStuff(*a_x, &a_y, &b_z);
    } 
    // ... and so on for all eight combinations of A and B.
}

But that's some pretty annoyingly repetitive code, and if I have several functions along the lines of doStuff it will quickly get unmanageable, especially if any of them have four or more arguments.

So my question is, is there any way to avoid this repetitive code? It seems like some further clever use of templates should be able to eliminate it, but I can't quite see how to do it.

解决方案

The title of your question: "static dispatch" - unfortunately your problem requires finding out the type at runtime... You want to identify the actual type from some base class pointers. This will work only at runtime.

Using dynamic_cast is a hack, the usage of dynamic_cast along usually means bad OO design. BTW, my bet would be that using dynamic_cast (dynamic_cast can be pretty slow!) and then a static function call is slower than simply making f() virtual and putting it to your base class and calling it that way.

Of course your case is a bit more special, you want to perform something else for all 8 cases but this is a piece of dirt work you won't be able to solve elegantly with brief code in C++ in my opinion. It is possible to forge a solution that is less error prone/easy to extend or one that is better in performance but none of them will be brief and/or elegant. Your current doStuff() implementation is rather a less error prone "bulletproof" solution, another one ugly solution has just came to my mind that would have good performance even for a lot of derived classes and class combinations (own type enum with own gettype + switch) but that is ugly as hell.

Summing it up: This problem has no nice solution in C++. You have to rethink your design or you have to live with ugly code when it comes to problems that can not be solved elegantly in C++: for example in case of serialization you often find ugly code like this...

这篇关于使用模板的C ++静态分派的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆