【问题标题】:Function member pointer with private base具有私有基数的函数成员指针
【发布时间】:2011-10-20 01:17:28
【问题描述】:

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

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

【问题讨论】:

  • 你有class base_der : private base
  • @SerbanLupu 你的例子很清楚,让我们感到困惑。我现在终于明白你的问题了,我无法解释。我已经修改了这个问题(希望)让它更清楚。 我还更正了一个错误:你在派生类中写了base::print;,但你需要写using base::print;
  • @KonradRudolph "你在派生类中写了base::print;,但你需要写using base::print;" 这是历史语法,在using关键字之前.
  • @0A0D 是的,但为什么这是一个问题是问题所在。这应该不是问题。

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


【解决方案1】:

我认为有一些交互问题会导致错误:

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

C++03 7.3.3 “使用声明”

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

请注意,虽然名称被带入新的“区域”,但它是同义词 - 名称所指的类型是相同的。所以,我认为在您的示例中,名称 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 是 D 的不可访问(第 11 条)、模棱两可(10.2)或虚拟(10.1)基类,则需要这种转换的程序是不正确的。转换的结果与发生转换之前的成员指针引用相同的成员,但它引用基类成员,就好像它是派生类的成员一样。结果引用了 D 的 B 实例中的成员。由于结果的类型为“指向类型为 cv T 的 D 成员的指针”,因此可以使用 D 对象取消引用。结果与指向 B 的成员的指针被 D 的 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++ 中的某些东西是更复杂或不太为人所知的领域之一时,这说明了很多。

【讨论】:

  • 不错的分析。我认为这符合我从 clang 中看到的行为/错误。
  • 类基仍然不是。”这没有意义。
  • @curiousguy:我指的是标准中的这一点:“如果 B 是 D 的不可访问(第 11 条)、模棱两可(10.2)或虚拟(10.1)基类,则程序需要此转换格式不正确。”
  • @MichaelBurr 无法访问base_der基类 base,而不是base 类。 base 类(类型)没有可访问性,并且类的名称“base”在此上下文中是可访问的。
【解决方案2】:

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

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

所以改变成员函数的类型是可行的,至少在clang和gcc中:

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

【讨论】:

  • "改变成员函数的类型有效" 是的,它有效,并且是定义良好的 C++,它也是错误的,因为它破坏了封装。 b>
【解决方案3】:

因为,

class base_der : private base

继承是private。所以base 无法访问base_der。将其更改为 public 即可。

【讨论】:

  • 不。我错了。我授予公众对“打印”功能的访问权限。我在 C++ Complete Reference 中读到了这一点。如果我使用主函数中注释的块正在工作。
  • @Serban - 但奇怪的是,该函数是 base_der 私有的,即使它对其他人是公开的!
  • "所以base 是不可访问的" 基类在这里显然是不可访问的(这是私有继承的重点)但是"为什么基类basebase_der 需要可访问”是真正的问题。
猜你喜欢
  • 2012-05-11
  • 2020-11-08
  • 1970-01-01
  • 2010-12-14
  • 1970-01-01
  • 2012-04-29
  • 1970-01-01
  • 2022-01-15
  • 1970-01-01
相关资源
最近更新 更多