我认为有一些相互作用的问题会导致错误:
- 指向成员的类型具有不直观的类型转换特性
- using 声明不影响带入范围的名称的类型
- 虽然名称
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++ 中的某些东西是更复杂或不太容易理解的领域之一时,这说明了很多。