Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Downcast: why: ‘A’ is an inaccessible base of ‘B’?

Unlike other examples of this error message i already have a pointer to A and want to retrieve the actual child class.

This kind of arrangement is part of some C++ wrapped C code there A is some POD C structure (whatswhy no dynamic cast) and test is some callback in C that calls C++ functionality and to retrieve the correct object the cast should be used. But to prevent C++ user code messing the C-Baseclass i would like to have the inheritance protected.

MSVC does not complain about this but g++ does!? Which one is correct from the standards point of view and why?

#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;
}

gives:

$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);
                                           ^
like image 650
vlad_tepesch Avatar asked Dec 20 '17 15:12

vlad_tepesch


2 Answers

From c++11 N3337 draft (a bit old but it's the one I have lying around) 5.2.9/11 (static_cast):

A prvalue of type “pointer to cv1 B,” where B is a class type, can be converted to a prvalue of type “pointer to cv2 D,” where D is a class derived (Clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D.

In this case, since you use protected inheritance there is no valid standard conversion from B to A so your static_cast is illegal (g++ is correct to diagnose it).

In this case since you're providing a c++ wrapper around a C API I think the simplest approach is to just stick with public inheritance and have a small amount of trust that your users won't abuse the C API directly if they've already consciously chosen to use your C++ API

like image 111
Mark B Avatar answered Oct 07 '22 15:10

Mark B


If you use protected inheritance, only derived types can be aware of that inheritance. As far as test is concerned, there is no relation between A and B. Since a static_cast can't cast pointers between unrelated types, you would need to reinterpret_cast instead. Or you may be able to provide B with a static method to preform this conversion, since B is aware of the inheritance. For example :

// 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();
}
}

Be sure that the object referred to by A* io_self is actually a B and not just a A or it's undefined behavior.

Are you sure protected is the right inheritance for you here? It seems like private inheritance might be clearer as there doesn't seem to be any intention of inheriting from B. You may also want to consider forgetting about inheritance and simply giving B a A member.

like image 43
François Andrieux Avatar answered Oct 07 '22 15:10

François Andrieux