1

I would like to access Base class members in an unqualified way (why? macro sorcery) from outside of the class itself. The strategy is do it in Derived class and cast the pointer-to-Base to pointer-to-Derived (even though the instance is not a Derived).

The code compiles and runs correctly as far as I tried: is this by standard or by accident (and UB by standard)? coliru link

#include<iostream>

struct Base{
    int a=3;
};

struct Derived: public Base{
    int getA(){
        // this scope has unqualified access to Base class members
        return a;
    }
};

int main(void){
    Base b;
    std::cerr<<((Derived*)(&b))->getA()<<std::endl;
}
curiousguy
  • 8,038
  • 2
  • 40
  • 58
eudoxos
  • 18,545
  • 10
  • 61
  • 110
  • 1
    Did you want a [tag:language-lawyer]-type answer (quote the standard) or would a reference to [cppreference.com](https://en.cppreference.com/w/cpp/language/static_cast) suffice? – JaMiT Dec 14 '19 at 07:21
  • There are legal ways to access private members: https://stackoverflow.com/questions/22624503/how-to-get-a-file-descriptor-from-a-stdbasic-ios-for-clang-on-os-x/39836224#39836224 – Alan Birtles Dec 14 '19 at 08:14
  • @JaMiT: I need the implementation to not work even when the compiler gets smarter, so language-lawyer type of answer gives me guarantee that it won't break in the future. – eudoxos Dec 14 '19 at 08:24
  • @AlanBirtles: I don't need to access private members (they are all public) but I need unqualified access outside of the class scope. – eudoxos Dec 14 '19 at 08:25
  • Perhaps the real problem is the macro sorcery. – n. m. could be an AI Dec 14 '19 at 08:32
  • @n.'pronouns'm. OT. – eudoxos Dec 14 '19 at 08:40

2 Answers2

5

The cast (Derived*)(&b) will be equivalent to static_cast<Derived*>(&b) which has undefined behavior if the object that &b points to is not actually a subobject of a Derived object. See [static.cast]/11 of the current C++ standard draft (equivalent language exists at least since C++11).

Your program therefore has undefined behavior, since there isn't any Derived object created at all and b is not a subobject of a Derived object.

Note that the cppreference page on static_cast is not explicit in stating that the cast itself has undefined behavior, but it has, as quoted above.

walnut
  • 21,629
  • 4
  • 23
  • 59
  • Thanks! Any other way to access members directly outside of the class scope? (`friend`, `using` magic or something similar?) – eudoxos Dec 14 '19 at 07:50
  • @eudoxos I suppose a reference declaration `auto& a = b.a;` does not fit your requirements? I don't have any better suggestion at the moment. Calling a member function of a non-existent object doesn't work in either case, even if you manage to obtain a pointer of correct type without UB in some way. – walnut Dec 14 '19 at 07:52
  • Good point about references, I will just have to declare them one by one using extra preprocessor sorcery ;) – eudoxos Dec 14 '19 at 09:33
1

I guess your code will run well. because it truely has the "a" member variable.

struct Derived: public Base{
    int c;
    int getA(){
        // this scope has unqualified access to Base class members
        return a;
    }
    int getC(){            
        return c;
    }
};

int main(void){
    Base b;
    std::cerr<<((Derived*)(&b))->getC()<<std::endl;
} 

This code above will run with error, because there no "c" member in the variable "b" whose type is truely Base class. The function getB called by function pointer, It don't belong any instance variable, so that doesn't metter.

cmf41013
  • 107
  • 1
  • 9
  • Yes, it will run, but is it legally correct code? That's the question. – eudoxos Dec 14 '19 at 09:36
  • not recommending since (a) low readability , (b) the cast is dengerous, (c) prone to error when you accidentally use like the getC function. Theoretically you can access any variable by address or force cast to access if you are clear about it , but it's a really bad idea. – cmf41013 Dec 14 '19 at 11:54
  • Actually, it's legal. – cmf41013 Dec 14 '19 at 12:04
  • Can you say why? You are contradicted by https://stackoverflow.com/a/59333141/761090 who is quoting the standard showing it is UB. I was using "legal" to mean "not-UB"; do you mean it in some other way, like parsable or compilable? – eudoxos Dec 14 '19 at 12:16
  • Becase the memory model of the Derived class instance is same to the instance of Base class – cmf41013 Dec 14 '19 at 12:21
  • Please quote the standard. Your opinion does not matter, really. I know they have the same memory layout. – eudoxos Dec 14 '19 at 12:24
  • I have taste nothing about your question. The standard won't tell you a bad idea. The "UB" the link talk about is under virtual base. – cmf41013 Dec 14 '19 at 13:20
  • 1
    Well... `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.` ...`Otherwise, the behavior is undefined.` Sorry ending discussion going nowhere here. – eudoxos Dec 14 '19 at 13:46
  • https://en.cppreference.com/w/cpp/language/static_cast, look at "2)" in "Explanation" and "static downcast" in example. On the other hand, force cast is different from static_cast. – cmf41013 Dec 15 '19 at 06:39