我认为有一些相互作用的问题导致了该错误:
- 指向成员类型的指针具有不直观的类型转换特征
- using 声明不会影响进入作用域的名称的类型
- 而名字
base_der::print
是可访问的,类base
仍然不是,在尝试转换指向成员的指针时,指向成员类型的指针中类的实际类型是考虑因素的一部分。
C++03 7.3.3“using 声明”
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 是 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
object.
无论如何,虽然我认为问题的根源是这个转换问题,但我认为您会收到有关可访问性的抱怨,因为当编译器尝试处理转换时,它首先检查base
- 即使这个名字base_der::print
(这是一个别名base::print
) 是可访问的,因为using
声明、类base
仍然不是。
免责声明:此分析来自对指向成员类型的指针的细微差别缺乏经验的人。它们是 C++ 的一个领域,非常复杂,除了最简单的场景之外很难使用,并且显然存在很多可移植性问题(请参阅 Doug Clugston 的文章,http://www.codeproject.com/KB/cpp/FastDelegate.aspx http://www.codeproject.com/KB/cpp/FastDelegate.aspx,它已经足够老了,现在很多问题可能已经得到解决,但我怀疑它们还没有)。
当你说 C++ 中的某些东西是更复杂或不太容易理解的领域之一时,这已经说明了很多。