Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/161.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 公共访问声明不影响成员函数指针?_C++_Inheritance_Language Lawyer - Fatal编程技术网

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

C++ 公共访问声明不影响成员函数指针?,c++,inheritance,language-lawyer,C++,Inheritance,Language Lawyer,我对g++(5.1版)下的访问声明有一个问题 类基 { 公众: void doStuff(){} }; 派生类:私有基 { 公众: //使用旧的访问声明(不使用)会发出警告 //并导致相同的编译错误 使用Base::doStuff; }; 模板 void exec(C*C,Func-Func) { (c->*func)(); } int main() { 导出d; //在这之前,一切都很顺利 d、 doStuff(); //由于某些原因,我无法访问函数指针 exec(&d,&Derived::d

我对g++(5.1版)下的访问声明有一个问题

类基
{
公众:
void doStuff(){}
};
派生类:私有基
{
公众:
//使用旧的访问声明(不使用)会发出警告
//并导致相同的编译错误
使用Base::doStuff;
};
模板
void exec(C*C,Func-Func)
{
(c->*func)();
}
int main()
{
导出d;
//在这之前,一切都很顺利
d、 doStuff();
//由于某些原因,我无法访问函数指针
exec(&d,&Derived::doStuff);
}
g++无法编译上述代码,原因是:

test.cpp:在“void exec(C*,Func)[其中C=Derived;Func=void(Base::*)()]”的实例化中: 测试。cpp:24:27:从这里开始需要
test.cpp:17:4:错误:“Base”是“Derived”的不可访问的基 (c->*func)()

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


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

问题在于
&Derived::doStuff
实际上不是指向类
Derived
成员的指针。从[expr.unary.op]:

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

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

为了解决重载问题,由using声明引入到 派生类将被视为派生类的成员

这就是
d.doStuff()
起作用的原因。但是,通过函数指针,您试图调用
派生的
对象上的
成员函数。这里没有重载解析,因为您直接使用函数指针,所以基类函数将不可访问

您可能认为可以将
&Derived::doStuff
强制转换为“正确”类型:

exec(&d,static_cast(&Derived::doStuff));
但根据[conv.mem]也不能这样做,因为
Base
派生的
的不可访问的基:

“指向cv
T
类型的
B
成员的指针”类型的PR值,其中
B
是类类型,可以转换为 “指向cv
T
类型的
D
成员的指针”类型的prvalue,其中
D
B
的派生类(第10条)。如果
B
不可访问(第11条)
D
的不明确(10.2)或虚拟(10.1)基类,或虚拟基类的基类 类
D
,需要进行此转换的程序格式不正确


我猜原因是成员函数实际上不是派生类的一部分,而是基类的一部分。通过检查成员函数指针的类型并将其与指向基本成员函数的指针进行比较,可以从经验上证明这一点:

cout << typeid(&Derived::doStuff).name() << endl
  << typeid(& Base::doStuff).name() << endl;

cout根据标准[namespace.udecl]:

using声明在using声明出现的声明性区域中引入名称

如果using声明命名构造函数(3.4.3.1),则它将隐式命名 在类中声明一组构造函数,其中 出现使用声明(12.9);否则,在 使用声明是另一种语言中一组声明的同义词 命名空间或类

因此,您只是将
Base::doStuff
引入
派生的
区域,它仍然是
Base
的成员函数


然后
exec
被实例化为
exec
,但由于私有继承,它不能将
派生的*
强制转换为
基*

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

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:*)()
类型的成员函数的指针,标准说它做得很好。因此,我认为编译时错误是公平的。

compilewithclang++:D@GabrieldeGrimouard程序是错误的,clang也应该(而且确实)拒绝它。@Barry这是个笑话:(并且没有带-std=c++11的叮当声++),请在我的计算机上编译它computer@Barry所以我有一个问题。为什么要用clang版本3.0-6.2编译它?@GabrieldeGrimouard这是一个bug。例如,C-cast怎么样?它会混合吗?有解决方案吗?可以通过memberfunction指针调用成员吗?@JorenHeit:猜猜not@Barry字体你真的要走了这是一个很好的例子。@JorenHeit没有。没有指向成员函数的指针。总是有
std::function f=[d]{d->doStuff();};
示例不规范。
cout << typeid(&Derived::doStuff).name() << endl
  << typeid(& Base::doStuff).name() << endl;
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
};