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

标签 c++

以下代码无法在 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::f 的可访问性超出了 B 的视角?

最佳答案

是的,你的程序格式不正确。

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 的名称和限定名称查找拼写的查找 Busing-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/

相关文章:

c++ - 理解 C++ 中显式构造函数的问题

c++ - 从另一个exe运行exe并传递参数

c++ - 尝试将统计对象添加到 DoublyLinkedList

c++ - 如何绘制变亮的边框(外发光效果)?

c++ - sizeof 总是一样吗?

c++ - 在 C++ 中阅读几秒钟

c++ - 如何将 override 关键字添加到大型 C++ 代码库?

c++ - 为什么遍历 unordered_map 但不能更改其映射值?

c++ - 如何简化嵌套循环?

c++ - 在 VC++ 中链接 FFTW 时出错