获取指向从私有基类别名的成员函数的指针
Taking a pointer to a member function aliased from a private base class
以下代码无法在Linux上使用GCC 7.2.0和Clang 5.0.0进行编译。
#include <iostream>
struct A
{
void f()
{
std::cout << "Hello, world!n";
}
};
struct B : private A
{
using A::f;
};
int main()
{
B b;
void (B::*f)() = &B::f; // Error: 'A' is an inaccessible base of 'B'
(b.*f)();
}
这符合标准吗?B
中的公共使用声明不应该允许透明地获取指向B::f
的成员函数指针,而不是在B
的视角之外涉及A::f
的可访问性吗?
是的,您的程序格式不正确。
C++17 (N4659) [namespace.udecl]/16 (强调我的):
出于重载解析的目的,将using-声明引入派生类的函数视为派生类的成员。特别是,应将隐式
this
参数视为指向派生类而不是基类的指针。这对函数的类型没有影响,并且在所有其他方面,函数仍然是基类的成员。
换句话说,using-声明不会添加B
的成员,它只是为同一成员A::f
添加第二个名称。 可以通过名称查找选择第二个名称,并用于对名称的使用进行辅助功能检查,但除此之外,除了重载解析之外,它等效于原始成员。
[expr.unary.op]/3:
一元运算符的结果
&
是指向其操作数的指针。操作数应为左值或限定 ID。如果操作数是一个限定 id,命名某个类C
的非静态或变体成员m
类型为T
,则结果的类型为"指向类型T
的类C
成员的指针",并且是指定C::m
的 prvalue 。
因此,即使您使用的限定 idB::f
是用class B
的名称拼写的,并且限定名查找在B
中找到了 using 声明引入的名称,表达式名称的实际函数是A
的成员,因此表达式&B::f
的类型为 "指向类A
成员的指针,其类型为 () 返回void
", 或作为类型 IDvoid (A::*)()
. 您可以通过添加到示例中来验证这一点:
#include <type_traits>
static_assert(std::is_same<decltype(&B::f), void (A::*)()>::value, "error");
最后,在 [conv.mem]/2 中:
类型为"指向cv
T
类型B
的成员的指针"的 prvalue,其中B
是类类型,可以转换为类型为 "指向cvT
类型的D
成员的指针"的 prvalue ,其中D
是B
的派生类。如果B
是不可访问的、不明确的或D
的虚拟基类,或者是D
的虚拟基类的基类,那么需要这种转换的程序是格式不正确的。
因此,命名指向成员函数的指针是有效的,但将其从void (A::*)()
转换为void (B::*)()
则无效,因为无法从main
访问继承。
作为一种解决方法,除了成员函数本身之外,您还可以在B
中提供对指向成员函数的指针的访问:
struct B : private A
{
using A::f;
using func_ptr_type = void (B::*)();
static constexpr func_ptr_type f_ptr = &A::f;
};
或者,如果您真的必须,请使用 C 型演员表。 在某些情况下,允许 C 样式强制转换忽略类继承关系的可访问性,其中任何C++样式强制转换都无法使用相同的结果进行编译。 (reinterpret_cast
还可以将指向成员函数的任何指针转换为任何其他指针,但其结果未指定。
int main()
{
B b;
void (B::*f)() = (void (B::*)()) &B::f;
(b.*f)();
}
关于问题所附评论的说明:如果将main
更改为
int main()
{
B b;
auto f = &B::f;
(b.*f)();
}
然后如上所述void (A::*)()
f
的类型。 但是后来你遇到了[expr.mptr.oper]/2(再次强调我的):
二元运算符
.*
将其第二个操作数(类型为"指向T
成员的指针")绑定到其第一个操作数,该操作数应是类T
的 glvalue 或T
是明确且可访问的基类的类的 glvalue。
因此,您仍然会遇到一个问题,即成员函数与其原始类相关联,并且不能被视为B
的成员,除非在B
和任何朋友的范围内。
- 为类的成员创建别名
- 是否可以为模板类的模板函数成员设置别名?
- 将结构别名为其第一个成员是否严格违反别名
- 派生类中的成员字段别名(无访问器功能)
- 成员模板别名与另一个模板成员
- 使用使用成员类型别名的构造函数来推论类模板参数
- 为对象的成员提供别名
- 如何定义别名以通过结构访问枚举类成员
- 根据模板参数条件 C++17 创建成员别名
- 别名 私有子结构中的静态成员的定义
- 如何有效地将浮点数别名为数组的命名成员和元素
- 模板基类函数成员的别名
- 类模板专用化中的成员变量别名
- 我可以使用相同的名称为周围作用域中的类型声明成员类型别名吗
- 我如何使用静态内类成员的别名
- C :方法中的字符串成员别名
- 如何制作特定类的成员函数或变量(如 STL 容器)的别名
- 我可以给派生类中基类的成员取别名吗
- C++:它是如何工作的别名成员类
- 在结构体中别名成员变量是否接受匿名联合?