具有私有基础的函数成员指针

时间:2011-08-04 14:11:11

标签: c++ implicit-conversion member-function-pointers member-pointers private-inheritance

以下代码产生编译时错误:

  

'base::print':无法访问“base_der”中声明的私有成员

但是,我在派生类中创建了成员public。为什么这不起作用?

#include <iostream>

using namespace std;

class base
{
public:
    int i;
    void print(int i)
    {
        printf("base i\n");
    }
};

class base_der : private base
{
public:
    using base::print;
};

int main()
{
    // This works:
    base_der cls;
    cls.print(10);

    // This doesn't:    
    void (base_der::* print)(int);
    print = &base_der::print; // Compile error here
}

3 个答案:

答案 0 :(得分:5)

我认为有一些相互作用的问题会导致错误:

  1. 指向成员类型的指针具有不直观的类型转换特征
  2. using声明不会影响带入范围的名称的类型
  3. 虽然名称base_der::print是可访问的,但类base仍然不是,并且试图将指针转换成成员,指针中的类的实际类型 - 会员类型是考虑的一部分。
  4. C ++ 03 7.3.3“使用声明”

      

    using声明在声明区域中引入了一个名称,其中出现using声明。该名称是在其他地方声明的某个实体的名称的同义词。

    请注意,虽然名称被带入新的“区域”,但它是同义词 - 名称所指的类型是相同的。因此,我认为在您的示例中,名称base_der::print的类型为void (base::*)(int),而不是void (base_der::*)(int)类型。

    C ++ 03标准还说明了指针到成员类型之间的转换( 4.11“指向成员转换的指​​针”):

      

    类型“指向cv T类型B的成员的指针”的右值,其中B是类类型,可以转换为类型为“指向类型为cv T的D的成员的指针”的右值,其中D是B的派生类(第10节)如果B是不可访问的(第11条),D的模糊(10.2)或虚拟(10.1)基类,则需要这种转换的程序是不正确的。转换的结果引用与转换发生前指向成员的指针相同的成员,但它引用基类成员,就好像它是派生类的成员一样。结果引用D的B实例中的成员。由于结果具有类型“指向cv T类型的D的成员的指针”,因此可以用D对象取消引用它。结果与使用D的B子对象取消引用B成员的指针相同。

    另请注意7.3.3 / 13“使用声明”(重点补充):

      

    出于重载解析的目的,using-declaration引入派生类的函数将被视为派生类的成员。特别是,隐式this参数应被视为指向派生类而不是基类的指针。 这对函数的类型没有影响,在所有其他方面,函数仍然是基类的成员。

    现在,生成错误的代码示例:

    // This doesn't:    
    void (base_der::* print)(int);
    print = &base_der::print; // Compile error here
    

    正在尝试将“指向D成员的指针”转换为“指向B成员的指针” - 这是一个错误方向的转换。如果你想一下,你就会明白为什么这个方向的转换不安全。类型为“指向B成员的指针”的变量可能不会与与class D有关的对象一起使用 - 但是如果调用类型为“指向D成员的指针”的函数(这是{ {1}} is),它正确地期望void (base_der::* print)(int)指针指向this对象。

    无论如何,虽然我认为问题的根源是这个转换问题,但我认为你得到了关于可访问性的抱怨,因为当编译器试图处理转换时,它首先检查{{1的可访问性 - 即使名称Dbase的别名)因base_der::print声明而无法访问,但类base::print仍然不可用。

    免责声明:此分析来自于对指针到成员类型的细微差别几乎没有经验的人。它们是C ++的一个复杂领域,除了在最简单的场景中很难使用,并且显然存在很多可移植性问题(参见Doug Clugston的文章http://www.codeproject.com/KB/cpp/FastDelegate.aspx,这篇文章已经足够老了很多这些问题可能已经解决了,但我怀疑他们不是)。

    当你说C ++中的某些东西是更复杂或更不易理解的领域之一时,那就说了很多。

答案 1 :(得分:4)

我不能说我知道为什么(也不能说明规范),但是clang的错误信息可能是有益的:

error: cannot cast private base class 'base' to 'base_der'

所以改变成员函数的类型至少在clang和gcc中起作用:

void (base::* print)(int);
print = &base_der::print; // works!

答案 2 :(得分:1)

这是因为,

class base_der : private base

继承是private。因此base无法访问base_der。将其更改为public,它将起作用。