垂头丧气:为什么:'A'是'B'不可访问的基数?

vla*_*sch 5 c++ inheritance language-lawyer

与该错误消息的其他示例不同,我已经有一个指向A并且想要检索实际子类的指针。

这种安排是某些C ++包装的C代码的一部分,其中包含A一些POD C结构(为什么没有动态转换),并且test是C中的一些回调,该回调调用C ++功能并检索应使用转换的正确对象。但是为了防止C ++用户代码使C-Baseclass混乱,我希望继承protected

MSVC不会对此抱怨,但是g ++会抱怨!?从标准的角度来看,哪一个是正确的,为什么?

#include <iostream>

using namespace std;

// plain C structure
struct A{
    int i;
};

// some C++ Wrapper class
struct B: protected A{
  A* get() { return this; }
  void print(){cout << i << endl;}
};



extern "C" {
  // C callback that gives it this pointer
  void test(A* io_self){
     auto b2 = static_cast<B*>(io_self);
     b2->print();
  }
}    

int main()
{
   B b;
   test(b.get());
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

给出:

$g++ -std=c++11 -o main *.cpp
main.cpp: In function ‘void test(A*)’:
main.cpp:21:43: error: ‘A’ is an inaccessible base of ‘B’
          auto b2 = static_cast<B*>(io_self);
                                           ^
Run Code Online (Sandbox Code Playgroud)

Mar*_*k B 4

来自 c++11 N3337 草案(有点旧,但它是我所拥有的)5.2.9/11 (static_cast):

\n\n
\n

\xe2\x80\x9c 类型的纯右值,指向 cv1 B,\xe2\x80\x9d,其中 B 是类类型,可以\n 转换为类型为 \xe2\x80\x9c 的纯右值,指向 cv2 D,\xe2\ x80\x9d 其中 D 是从 B 派生的类(第 10 条),如果从 \n \xe2\x80\x9c 指向 D\xe2\x80\x9d 的指针到 \xe2\x80\x9c 指向 B\ 的指针的有效标准转换xe2\x80\x9d 存在 (4.10),cv2 与 cv1 的 cv 限定相同或更高,并且 B 既不是 D 的虚拟基类,也不是虚拟基的基类\n D 类。

\n
\n\n

在这种情况下,由于您使用protected继承,因此没有从B到的有效标准转换A,因此您static_cast是非法的(g++ 可以正确诊断它)。

\n\n

在这种情况下,由于您要围绕 C API 提供 C++ 包装器,我认为最简单的方法是坚持使用公共继承,并稍微相信您的用户不会直接滥用 C API(如果他们已经使用过)有意识地选择使用您的 C++ API

\n