从私有基类获取别名的成员函数的指针

时间:2017-10-14 23:03:07

标签: 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::fB之外的qic(Value, x = Version, data = df, facets = FileName ~ Category, chart = 'c', main = 'File by Category', ylab = 'Category1', xlab = 'Version') 的可访问性。透视?

1 个答案:

答案 0 :(得分:5)

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

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

  

出于重载解析的目的, using-declaration 引入派生类的函数被视为派生类的成员。特别是,隐式this参数应被视为指向派生类而不是基类的指针。 这对函数的类型没有影响,在所有其他方面,函数仍然是基类的成员。

换句话说, using-declaration 不会添加B的成员,只会为同一成员A::f添加第二个名称。可以通过名称查找来选择第二个名称,并将其用于对名称使用的可访问性检查,但除此之外,除了重载解析之外,它等同于原始成员。

[expr.unary.op] / 3:

  

一元&运算符的结果是指向其操作数的指针。操作数应为左值或 qualified-id 。如果操作数是 qualified-id ,命名类型为m的某个类C的非静态或变体成员T,则结果为“指针”类型致CT的成员,并且是指定C::m的prvalue。

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

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

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

  

“指向{em> cv B <{1}}成员的类型的prvalue”,其中T是类类型,可以转换为“指向 cv B类型D成员的指针”的prvalue,其中TD的派生类。如果BB的不可访问,模糊或虚拟基类,或虚拟基类D的基类,则需要进行此转换的程序格式不正确。

因此,将指向成员函数的指针命名为有效,但将其从D转换为void (A::*)()则不然,因为void (B::*)()无法访问继承。

作为一种解决方法,除main中的成员函数本身外,您还可以访问指向成员函数的指针:

B

或者,如果你真的必须,使用C风格的演员表。在某些情况下,允许C样式转换忽略类继承关系的可访问性,其中没有任何C ++样式的转换可以使用相同的结果进行编译。 (struct B : private A { using A::f; using func_ptr_type = void (B::*)(); static constexpr func_ptr_type f_ptr = &A::f; }; 也可以将任何指向成员函数的指针转换为任何其他指针,但其结果未指定。)

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,如上所述。但是你遇到[expr.mptr.oper] / 2(再次强调我的):

  

二元运算符void (A::*)()将其第二个操作数绑定到第一个操作数,该操作数应为“.*成员的指针”类型,该操作数应为类T的glvalue或一类T是一个明确且可访问的基类。

因此,您仍然遇到成员函数与其原始类相关联的问题,除T和任何朋友的范围外,不能被视为B的成员。

相关问题