将不可访问的私有基类型的指针传递给派生类方法

时间:2013-03-11 15:28:06

标签: c++

此代码示例将描述我认为非直观的语言功能。

class A {
public:
  A() {}
};

class B: private A
{
public:
  B() {}
};

class C: public B
{
public:
  C() {}
  void ProcessA(A* a) {
  }
};

int main() {
  C c;
}

在Mac上使用Apple LLVM 4.2版编译此代码会产生

test.cc:16: error: ‘class A’ is inaccessible
test.cc:16: error: within this context

void ProcessA(A* a)替换void ProcessA(::A* a)会使其成为现状,但我不明白为什么我应该在这里使用绝对类名。 它是一种语言功能,可以避免某些错误,或者只是一个黑暗的C ++语法角落,比如要求在与其他模板参数化的模板中的尖括号(> >)之间放置空格。 谢谢!

1 个答案:

答案 0 :(得分:11)

人类谈话

我将从描述这里发生的事情开始 - 请原谅我,如果你已经知道这一点,但它为后续行动创造了必要的背景。

编译器将非限定A解析为::C::A(如果您自己在源级别进行更改,结果将相同)。由于::C::A无法访问,因此会发出错误消息。

您建议编译器检测到::C::A无法访问,然后应将对A的引用视为对::A的引用作为后备。但是,::C::A::A可能很容易就是两个完全不同的东西

自动猜测这里应该做什么不仅容易引入错误和/或拔毛¹,而且完全违背C ++的精神。

Standardese

直接从C ++ 11标准确认此行为符合要求并按设计进行。

§9/ 2说:

  

类名插入声明它的作用域中   在看到 class-name 之后立即执行。 类名也是   插入到类本身的范围内;这被称为   注射类名

这意味着在C类的范围内,A注入类名

§3.4/ 3声明 inject-class-name 是名称查找的候选者:

  

类的注入类名也被视为成员   用于名称隐藏和查找的那个类。

§3.4/ 1澄清了基础A的不可访问性并未阻止注入类名 A被考虑:

  

访问规则仅被视为名称查找和功能   重载决议(如果适用)已成功。

§11.1/ 5直接解释了正在讨论的确切情况:

  

[注意:在派生类中,将查找基类名称的查找    inject-class-name 而不是基类的名称   声明的范围。 注入类名可能更少   比它所在范围内的基类名称可访问   被宣布。 - 后注]

该标准也给出了这个例子,它等同于你的例子:

class A { };
class B : private A { };
class C : public B {
    A *p;   // error: injected-class-name A is inaccessible
    ::A *q; // OK
};

¹想象一下,如果A最初是public基础,后来在重构期间变为private会发生什么。还要设想::A::C::A是无关的。你会发现像a->foo()这样的调用(曾经工作过)会失败,因为foo不再可访问,但是a的类型已经改变了你的背后而你现在得到“没有方法foo”错误。咦?!?这当然远非可能发生的最坏情况。