2014-03-03 73 views
1

假设我有一些C++代码,看起来像这样:C++静态使用调度模板

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

这样做的目的是为了避免内循环中的虚函数调用f(),为了让编译器的优化。 (这显然是我的实际代码的简化版本,关于我的使用案例的详细信息,请参阅此more specific question)。

这工作正常,如果doStuff的参数的类型在运行时知道,但如果他们不那么它失败:

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

来解决这个问题(由this answer的建议),它看来我要建立明确的静态调度功能:

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. 
} 

但是,这是一些很烦人的重复代码,如果我有沿doStuff线多种功能,将很快得到不可收拾,特别是如果其中任何公顷有四个或更多的论点。

所以我的问题是,有什么办法可以避免这个重复的代码?看起来,模板的一些更巧妙的使用应该能够消除它,但我不知道如何去做。

+0

模板可能很难,但Boost的预处理器库可能会帮助您:http://www.boost.org/doc/libs/1_40_0/libs/preprocessor/doc/ –

+2

dynamic_cast将仅适用于具有vtable的层次结构类 –

+1

@Alex它似乎是好的,如果我添加一个虚拟的虚拟方法的基类 - 我已经编辑帖子。 – Nathaniel

回答

1

问题的标题是:“static dispatch” - 不幸的是,您的问题需要在运行时找到类型...您想从某些基类指针中识别实际类型。这只会在运行时才起作用。

使用dynamic_cast是一种破解,dynamic_cast的使用通常意味着糟糕的OO设计。顺便说一句,我敢打赌,使用dynamic_castdynamic_cast可能会非常慢!),然后静态函数调用比简单地将f()虚拟化并将其放到您的基类并调用它的方式要慢。

当然你的情况有点特别,你希望为所有8种情况执行其他的事情,但这是一件很脏的工作,你不能用C++中的简短代码来优雅地解决。有可能建立一个不容易出错/易于扩展的解决方案,或者一个性能更好的解决方案,但它们都不会简短和/或优雅。你目前的doStuff()实现是一个较不容易出错的“防弹”解决方案,另一个丑陋的解决方案刚刚出现在我的脑海中,即使对于很多派生类和类组合(自己的类型枚举与自己的gettype +开关)但那真是丑陋。

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

+0

这是不好的OO设计!不幸的是,糟糕的设计是SIMD并行性是编译器优化而不是语言特性的自然结果 - 这就是需要以这种方式构建它的原因。请注意,'f()'连续被调用100000次(可以由编译器进行矢量化),而'dynamic cast'只会调用一次,所以成本相对较低。但是,是的,我倾向于认为你可能是对的,没有优雅的方式来做到这一点。 (但它不能伤害要问。) – Nathaniel

0

这是一个经典的多派遣问题。它在文献中非常发达。其中一个解决方案是访问者模式(您需要将它应用两次,因为您有3个参数可用于分派)。另一个是map<tuple<type_info, type_info, type_info>, StuffDoer>(后一类应该有一个虚拟的doStuff方法,并对其中的所有参数执行dynamic_cast)。

+0

如果你有时间,你能指出我对第二种解决方案的样子吗?自90年代以来我没有使用STL,所以我很难想象代码。 – Nathaniel