在 C++ 中通过其他子 class 的朋友访问受保护的基 class 是否合法?

Is it legal to get access to protected base class via a friend of additional child class in C++?

假设我们有一些第三方库,其中 class B 使用 protected 继承从 A 派生。所以有一个 B 的对象,我们可能无法正常访问它的基础 A.

但是我们可以创建一些额外的 class C(永远不会构造任何对象)从 B 派生它并声明它与需要访问的函数的友谊A-B-对象的一部分。甚至有两个选项如何获得访问权限:

// some 3rd party library
struct A {};
struct B : protected A {};

// our code to get access to A
struct C : B { friend int main(); };

int main() {
    const B & b = B{};
    [[maybe_unused]] const A & a1 = static_cast<const C&>( b ); // #1: ok everywhere
    [[maybe_unused]] const A & a2 = b; // #2: ok in GCC and MSVC
}

带有 static_cast<const C&> 的选项 #1 适用于所有编译器。但它的格式是否正确(没有未定义的行为)?

选项 #2 更短,但它仅适用于 GCC 和 MSVC,演示:https://gcc.godbolt.org/z/zj146bKsb

鉴于 Clang 拒绝接受它,它是唯一正确满足此处标准的编译器吗?

But is it well formed (no undefined behavior)?

是未定义的行为。

[expr.static.cast] (emphasis mine)

2 An lvalue of type “cv1 B”, where B is a class type, can be cast to type “reference to cv2 D”, where D is a class derived from B, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. If B is a virtual base class of D or a base class of a virtual base class of D, or if no valid standard conversion from “pointer to D” to “pointer to B” exists ([conv.ptr]), the program is ill-formed. An xvalue of type “cv1 B” can be cast to type “rvalue reference to cv2 D” with the same constraints as for an lvalue of type “cv1 B”. If the object of type “cv1 B” is actually a base class subobject of an object of type D, the result refers to the enclosing object of type D. Otherwise, the behavior is undefined.

我们在铸造 b 时没有实际的 C。所以我们直接跳到最后一句话,调用 cast 本身是未定义的。

Given that Clang refuses to accept it, is it the only compiler that fulfills properly the standard here?

是的,是的。 Bmain没有特殊关系,没有友谊。所以 A 基地在那个范围内是不可访问的。如果所述基可访问,则只能将指向派生 类 的指针(和引用)转换为指向基 类 的指针(和引用):

[conv.ptr]

3 A prvalue of type “pointer to cv D”, where D is a complete class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class ([class.derived]) of D. If B is an inaccessible ([class.access]) or ambiguous ([class.member.lookup]) base class of D, a program that necessitates this conversion is ill-formed. [...]