6

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

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
}
4

3 回答 3

5

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

  1. 指向成员的类型具有不直观的类型转换特性
  2. using 声明不影响带入范围的名称的类型
  3. 虽然名称base_der::print是可访问的,但类base仍然不能访问,并且在尝试转换指向成员的指针时,指向成员类型的类的实际类型是考虑的一部分。

C++03 7.3.3 “使用声明”

using-declaration 将名称引入到 using-declaration 出现的声明性区域中。该名称是在别处声明的某个实体名称的同义词。

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

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

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

另请注意 7.3.3/13“使用声明”(强调添加):

出于重载决议的目的,通过 using 声明引入派生类的函数将被视为派生类的成员。特别是,隐含的 this 参数应被视为指向派生类而不是基类的指针。这对函数的类型没有影响,并且在所有其他方面,该函数仍然是基类的成员。

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

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

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

无论如何,虽然我认为问题的根源是这个转换问题,但我认为您正在抱怨可访问性,因为当编译器尝试处理转换时,它首先检查 - 的可访问性base,即使名称base_der::print(这是 的别名base::print)由于using声明而可以访问,类base仍然不是。

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

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

于 2011-08-04T18:03:59.887 回答
4

我不能说我知道为什么(我也不能跟规范说话),但是 clang 的错误信息可能是有启发性的:

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

因此,至少在 clang 和 gcc 中更改成员函数的类型是有效的:

void (base::* print)(int);
print = &base_der::print; // works!
于 2011-08-04T15:24:27.247 回答
1

这是因为,

class base_der : private base

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

于 2011-08-04T14:15:30.557 回答