2012-05-11 14 views
1

我现在有一个C++接口转换切换至非虚拟多态性

void foo() ... 

和大量implementions的

void A::foo(); 
void B::foo(); 

目前foo的定义如下

struct Wrapper 
{ 
    void foo() 
    { 
     if(state == STATE_A) a.foo(); 
     else if(state == STATE_B) b.foo(); 
    } 
    union { 
     A a; 
     B b; 
    }; 
    State state; 
} 
//.... 
Wrapper a(STATE_A, existingA); 
Wrapper b(STATE_B, existingB); 
a.foo(); b.foo(); 

是否有干净的方式来做到这一点?我有多个foo()函数和多个A/B类。它变得单调乏味/容易出错写所有的情况。

请注意,我不能使用虚函数(它运行在N^5循环内......每秒执行1000万次执行)。我希望编译器内联这个,很难。

我曾想过收集的公司,B公司等一起,在一个面向数据的方式计算它们,但不幸的是我不能做到这一点(由于算法问题)

+1

'它变得单调乏味/容易出错写出所有的情况。'这就是当你放弃语言特性并选择直接编码时发生的情况。这就是为什么这些语言特征首先存在:因为有人厌倦了写出样板。 –

+1

要添加,这比使用虚拟调度更好吗?即使这是使用索引(如数组查询)而不是if-else链进行优化的,这相当于使用虚拟调度(就大多数编译器而言)。也就是说,STATE_X代表一个vtable,而Wrapper :: foo就像vtable查找一样...... –

+3

你真的用虚拟函数测量了简单的实现吗? 3-5“如果”条件可以很容易地匹配虚拟功能调用的成本。并且不能保证内联真的发生如果功能增长足够大... –

回答

2

我想编译器内联这很难。

这不会发生。

您正在使用运行时多态性。根据定义,编译器无法知道哪些函数将在通话时调用。您将支付虚拟调度,无论是手动执行还是让编译器执行此操作。

绝对大多数内联你会得到的是在对成员函数的调用中。它仍然需要基于内存访问(获取“类型”)来进行“内联”部分的条件分支。并且每添加一个新的“状态”,都会将条件再加上条件。最好的情况是,这将成为一个状态表......它不同于虚拟函数指针:它从一个内存地址中获取,并使用它来分支到一段特定的代码。

就像一个vtable指针,只有你浪费你的时间来实现编译器可以为你做的事情。

我强烈建议你简介这不是简单地假设你的手写方法可以击败编译器。


如果你已经决定放弃语言级的多态性,那么你应该使用一个boost.variant和适当的游客来代替。您的代码应该是这样的:

typedef boost::variant<A, B> Wrapper; 

struct FooVisitor : public boost::static_visitor<> 
{ 
    template <typename T> void operator()(T &t) {t.foo()}; 
}; 

你将不得不作出一个FooVisitor你想调用各项功能。要调用它,你这样做:

Wrapper a = existingA; 
boost::apply_visitor(FooVisitor(), a); 

显然,你可以包装在一个简单的函数:

void CallFoo(Wrapper &a) {boost::apply_visitor(FooVisitor(), a);} 

事实上,你可以让它们的整个模板系列:

template<typename Visitor> 
void Call(Wrapper &a) {boost::apply_visitor(Visitor(), a);} 

请注意,不允许参数传递(您必须将参数存储在访问者本身中),但它们可以有返回值(您必须将返回类型放入您的visi的boost::static_visitor<Typename_Here>声明中TOR)。

另请注意,boost::variant对象具有值语义,因此副本将复制内部对象。您也可以使用boost::get()语法来获得实际的类型,但我不会建议它,除非您真的需要。只是使用游客。

+0

是的,应该明确这一点。 – jameszhao00

+0

另外,我目前在连续分配的内存中持有Wrapper。我可以使用placement new,并且使用max(sizeof(A),sizeof(B))大小来容纳多态对象...但是它也会变得有点难看。 – jameszhao00

1

你有两种选择。您可以在编译时进行函数选择,也可以在运行时进行选择。如果是运行时间,你不会比现有的虚拟机制做得更好。如果是编译时,您需要针对每种类型使用不同的代码,但您可以使用模板来自动执行该过程。

template<typename T> 
struct Wrapper 
{ 
    void foo() 
    { 
     t.foo(); 
    } 
    T t; 
}; 

当然这个例子是高度抽象的,我无法看到使用Wrapper类和模板直接输入之间的任何差异。你必须充实你的例子以获得更好的答案。