以下代码无法在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
之外的qic(Value,
x = Version,
data = df,
facets = FileName ~ Category,
chart = 'c',
main = 'File by Category',
ylab = 'Category1',
xlab = 'Version')
的可访问性。透视?
答案 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
,则结果为“指针”类型致C
类T
的成员,并且是指定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
是类类型,可以转换为“指向 cvB
类型D
成员的指针”的prvalue,其中T
是D
的派生类。如果B
是B
的不可访问,模糊或虚拟基类,或虚拟基类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
的成员。