C++中通过额外子类友元访问受保护基类是否符合语言标准?
问题背景
假设我们使用的第三方库中存在类B以protected继承方式派生自A,因此正常情况下无法通过B类对象访问其基类A的部分。
我们可以额外创建一个永远不会实例化的类C,使其继承自B,同时将需要访问B对象中A部分的函数声明为C的友元,目前有两种可选的访问实现方式:
// 第三方库代码 struct A {}; struct B : protected A {}; // 我们用于访问A的自定义代码 struct C : B { friend int main(); }; int main() { const B & b = B{}; [[maybe_unused]] const A & a1 = static_cast<const C&>( b ); // 方案1:所有编译器都可正常编译 [[maybe_unused]] const A & a2 = b; // 方案2:仅GCC、MSVC可正常编译 }
方案1是否为符合标准的良构代码、不存在未定义行为?
是,该写法完全符合C++标准要求,无任何未定义行为。
原因如下:
- 访问权限层面:
C公开继承自B,作为B的派生类,C有权访问B的protected基类A;而main被声明为C的友元,因此main中有权访问C实例中的A基类子对象。 - 类型转换层面:
static_cast<const C&>(b)将B类型的左值转换为C类型的左值,后续隐式转换到A&时,访问的是B对象中真实存在的A基类子对象,全程没有访问C类特有的任何成员,因此不存在类型转换导致的未定义行为。这个技巧也被业内称为protected member hack,是被标准认可的合法操作。
Clang是否是唯一正确遵循C++标准的编译器?
是,GCC和MSVC对方案2的支持属于非标准的编译器扩展,Clang的行为才严格匹配C++标准要求。
原因如下:
方案2尝试直接将B类型引用隐式转换为A类型引用,此时访问权限的检查在main函数的上下文中进行,main既不是B的派生类也不是B的友元,没有权限访问B的protected基类子对象,因此隐式转换本身是非法的,应该被编译器拒绝。GCC和MSVC在这里放宽了权限检查规则,属于不符合标准的实现。
内容的提问来源于stack exchange,提问作者Fedor
相关产品推荐
相关产品推荐

