垂头丧气:为什么:'A'是'B'无法进入的基地?

时间:2017-12-20 15:17:05

标签: c++ inheritance language-lawyer

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

这种安排是一些C ++包装的C代码的一部分A是一些POD C结构(whatswhy没有动态转换)和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;
}

给出:

$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);
                                           ^

2 个答案:

答案 0 :(得分:4)

如果使用protected继承,则只有派生类型才能知道该继承。就test而言,AB之间没有关系。由于static_cast无法在不相关的类型之间转换指针,因此您需要reinterpret_cast。或者,您可以为B提供静态方法来执行此转换,因为B知道继承。例如:

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


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

确保A* io_self引用的对象实际上是B,而不仅仅是A或未定义的行为。

你确定protected对你来说是正确的继承吗?似乎private继承似乎更清晰,因为似乎没有任何继承B的意图。您可能还想考虑忘记继承并简单地给B A成员。

答案 1 :(得分:3)

来自c ++ 11 N3337草案(有点旧,但它是我躺在那里的)5.2.9 / 11(static_cast):

  

类型为“指向cv1 B的指针”的prvalue,其中B是类类型,可以是   转换为“指向cv2 D的指针”的prvalue,其中D是一个类   如果是有效的标准转换,则从B派生(第10条)   “指向D的指针”到“指向B的指针”存在(4.10),cv2是相同的   cv-qualification为,或者比cv1更高的cv资格,而B是   既不是D的虚基类,也不是虚基的基类   D级。

在这种情况下,由于您使用protected继承,因此BA之间没有有效的标准转换,因此您的static_cast是非法的(g ++对于诊断是正确的) )。

在这种情况下,由于您提供了围绕C API的c ++包装器,我认为最简单的方法是坚持使用公共继承,并且对用户不会直接滥用C API具有少量信任如果他们已经有意识地选择使用您的C ++ API