首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >公共访问声明不会影响成员函数指针?

公共访问声明不会影响成员函数指针?
EN

Stack Overflow用户
提问于 2015-07-20 21:47:58
回答 3查看 495关注 0票数 18

在g++ (5.1版)下,我有一个关于访问声明的问题。

代码语言:javascript
复制
class Base
{
public:
    void doStuff() {}
};

class Derived : private Base
{
public:
    // Using older access declaration (without using) shoots a warning
    // and results in the same compilation error
    using Base::doStuff;
};

template<class C, typename Func>
void exec(C *c, Func func)
{
    (c->*func)();
}

int main()
{
    Derived d;
    // Until here, everything compiles fine
    d.doStuff();
    // For some reason, I can't access the function pointer
    exec(&d,&Derived::doStuff);
}

g++无法编译上述代码,出现以下错误:

test.cpp: In实例化‘void exec(C*,Func) with C= Derived;Func =void(Base::*)()’:test.cpp:24:27:此处需要

函数:错误:‘base’是‘test.cpp:17:4’(c->*func)()的一个不可访问的基;

即使函数本身可以被调用(d.doStuff();),也不能使用指针,即使我声明函数是可以从外部访问的。私有继承在某种程度上也很重要,因为Derived类选择只公开基类中的特定成员集,基类是接口实现IRL。

注意:这是一个关于语言的问题,不是类设计的问题。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-07-20 22:11:12

问题是&Derived::doStuff实际上并不是指向类Derived的成员的指针。来自expr.unary.op

一元&运算符的结果是指向其操作数的指针。操作数应为左值或限定id。如果操作数是一个限定id,用于命名类型为T的某个类C的非静态或可变成员m,则结果的类型为“指向类型为T的类C的成员的指针”,并且是一个指定为C::m的prvalue。

doStuff不是Derived的成员。它是Base的成员。因此,它具有指向Basevoid (Base::*)()成员的类型指针。using-声明在这里所做的仅仅是帮助来自namespace.udecl的重载解析

出于重载解析的目的,通过使用声明引入派生类的函数将被视为派生类的成员。

这就是d.doStuff()工作的原因。但是,通过函数指针,您正在尝试调用Derived对象上的Base成员函数。这里没有重载解析,因为你直接使用了一个函数指针,所以基类函数将是不可访问的。

您可能认为只需将&Derived::doStuff转换为“正确”类型:

代码语言:javascript
复制
exec(&d, static_cast<void (Derived::*)()>(&Derived::doStuff));

但根据conv.mem,您也不能这样做,因为BaseDerived的一个不可访问的基础

“指向类型为cv TB的成员的指针”类型的PRV值,其中B是类类型,可以被转换为“指向类型为cv TD的成员的指针”类型的PR值,其中DB的派生类(第10条)。如果B不可访问(第11条)的、歧义(10.2)或D的虚拟(10.1)基类,或者是D的虚拟基类的基类,则需要进行此转换的程序是格式错误的。

票数 19
EN

Stack Overflow用户

发布于 2015-07-20 22:22:27

根据标准namespace.udecl

使用声明将一个名称引入到声明区域中,在该区域中将出现使用声明。

如果使用声明命名了一个构造函数(3.4.3.1),它会隐式地在出现该使用声明的类中声明一组构造函数(12.9);否则,在使用声明中指定的名称是另一个名称空间或类中的一组声明的同义词。

所以您只是将Base::doStuff引入Derived区域,它仍然是Base的成员函数。

然后exec被实例化为exec<Derived, void (Base::*)()>,但由于私有继承,它不能将Derived*转换为Base*

票数 1
EN

Stack Overflow用户

发布于 2015-07-20 22:23:53

来自C++11标准,§7.3.3 namespace.udecl,18:

代码语言:javascript
复制
class A
{
private:
    void f( char );
public:
    void f( int );
protected:
    void g();
};
class B : public A
{
    using A::f; // error: A::f(char) is inaccessible
public:
    using A::g;
    // B::g is a public synonym for A::g
};

B::g是A::g部件的公共同义词。当你获取Derived::doStuff的地址时,GCC创建了一个指向void(Base::*)()类型的成员函数的指针,而标准说它做得很好。因此,我认为编译时错误是公平的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31518214

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档