gpt4 book ai didi

c++ - 获取指向私有(private)基类别名的成员函数的指针

转载 作者:搜寻专家 更新时间:2023-10-31 00:28:03 24 4
gpt4 key购买 nike

以下代码无法在 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 中的公共(public) using 声明不应该允许透明地获取指向 B::f 的成员函数指针,而不是涉及 A::fB 的视角之外的可访问性?

最佳答案

是的,你的程序格式错误。

C++17 (N4659) [namespace.udecl]/16(强调我的):

For the purpose of overload resolution, the functions that are introduced by a using-declaration into a derived class are treated as though they were members of the derived class. In particular, the implicit this parameter shall be treated as if it were a pointer to the derived class rather than to the base class. This has no effect on the type of the function, and in all other respects the function remains a member of the base class.

换句话说,using-declaration 没有添加 B 的成员,它只是为相同的成员 A::f 添加了第二个名字。第二个名称可以通过名称查找来选择,并用于对该名称的使用进行可访问性检查,但除此之外,除了为重载解析所指出的之外,它等同于原始成员。

[expr.unary.op]/3:

The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. If the operand is a qualified-id naming a non-static or variant member m of some class C with type T, the result has type "pointer to member of class C of type T" and is a prvalue designating C::m.

因此,即使您使用的 qualified-id B::f 拼写为 class B 的名称和限定名称查找在B中找到using-declaration引入的名字,表达式命名的实际函数是A的成员,所以表达式&B::f 具有类型“指向返回 void 的 () 类型函数的类 A 成员的指针”,或者作为 type- idvoid (A::*)()。您可以通过添加到您的示例来验证这一点:

#include <type_traits>
static_assert(std::is_same<decltype(&B::f), void (A::*)()>::value, "error");

最后,在 [conv.mem]/2 中:

A prvalue of type "pointer to member of B of type cv T", where B is a class type, can be converted to a prvalue of type "pointer to member of D of type cv T", where D is a derived class of B. If B is an inaccessible, ambiguous, or virtual base class of D, or a base class of a virtual base class of D, a program that necessitates this conversion is ill-formed.

所以命名指向成员函数的指针是有效的,但是将它从 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)();
}

那么 f 的类型就是 void (A::*)() ,如上所述。但随后您遇到了 [expr.mptr.oper]/2(再次强调我的):

The binary operator .* binds its second operand, which shall be of type "pointer to member of T" to its first operand, which shall be a glvalue of class T or of a class of which T is an unambiguous and accessible base class.

因此,您仍然有一个问题,即成员函数与其原始类关联并且不能被视为 B 的成员,但在 B 的范围内和任何 friend 。

关于c++ - 获取指向私有(private)基类别名的成员函数的指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46750163/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com