2016-07-28 102 views
-1

我明白你为什么不能简单地将派生类成员函数指针转到基类成员函数指针,如解释here演员派生虚拟覆盖基地纯虚拟成员

但是,鉴于这个片段:

struct base 
{ 
    virtual void foo() = 0; 
}; 

struct derived : base 
{ 
    void foo() override {}; 
}; 

struct invoker 
{ 
    typedef void(base::*target)(); 

    invoker(base* b, target t) 
    { 
     (b->*t)(); 
    } 
}; 

template<typename B, typename D> 
void (B::*cast(void (D::*method)()))() 
{ 
    return static_cast<void(B::*)()>(method); 
} 

derived d; 
invoker bad(&d, &derived::foo); //C2664 
invoker good(&d, cast<base>(&derived::foo)); 

我想问问有没有可能使编译器理解它来装饰基函数签名是一个纯虚方法,它会某处跨层次结构来实现(否则我不能构造一个B类型的对象)?我明白为什么我不能用普通函数做到这一点,但恕我直言,在纯虚函数的情况下,编译器有保证它会被执行(如果没有完成,我会得到关于类B的错误,而不是关于投)。

+1

目前还不清楚你要求什么样的装饰。如果你是C++设计委员会的最高领导,你会怎么做?不要担心一致性或正确性,只需显示您想要添加到C++中的内容即可。如果你只是想告诉编译器在'base'中存在'derived :: foo',你可以直接写'&base :: foo'。 –

+0

@ n.m。我想避免做出明确的演员:D但它更像是“我有没有意识到某种方式”的问题类型。 –

+1

不,不要演员。只需从&base :: foo开始。 '&derived :: foo'做什么'&base :: foo'不能做什么? –

回答

2

没有必要操纵&derived::foo的类型。人们可以使用&base::foo来代替。

成员函数的指针尊重虚拟性。此调用

base* pBase = new derived; 
auto pFoo = &base::foo; 
(pBase->*pFoo)(); 

实际上将调用derived::foo,完全像一个简单的通话pBase->foo()会。

0

即使有保证,它可能会使用仅在derived中声明的其他数据成员。一个可能的解决方案是使用函数指针并通过this作为第一个参数(这也显示了为什么你不能通过virtual)。

+0

由于我传递'base *',因为运行时多态性,最终会导致正确的重写,所以我无法访问任何不存在的东西。或者我误解了你? –

+0

添加一个'int i;'来派生并做'i = 0xDEADBEEF;'到'derived :: foo()'来看看我的意思。它反过来工作:你可能会传递'Base'函数指针,其中'derived'是所期望的。注意,*你知道你正在传递'derived *',但是C++编译器可能不知道它。 – lorro

+0

不,我不能构建'Base'的实例,因为它有一个纯虚拟成员。我不是在谈论一般情况,而是谈论演员的目标是“基地”的纯虚拟成员。 –

0

考虑下面的钻石层次:

struct base { 
virtual void foo() = 0; 
}; 

struct D1 : public virtual base { 
virtual void foo() override; 
}; 

struct D2 : public virtual base { 
virtual void foo() override; 
}; 

struct Derived : public virtual D1, D2 { 
virtual void foo() final; 
}; 

现在考虑一个向上转型是从所允许的情况下派生:: *立足:: *。应该调用哪个函数?编译器会丢失有关您要调用的D1 :: foo,D2 :: foo或Derived :: foo中的哪些信息,因为这些信息已被丢弃。为了避免这种模棱两可的情况,不允许使用upcast。

+0

我想我的失败点在于vtable查找也发生在函数指针上(并且在那种情况下,实际上会调用正确的方法),但我想它不会发生。 –

+0

“_哪个函数应该被调用?_”'foo()',我猜想 – curiousguy