On Wednesday, June 26, 2019, 11:53:30 AM PDT, Chris Angelico <rosuav@gmail.com> wrote:
I don't think you can define what "f->spam" means Well, you can, but only the first half of what it means, so I don't think this changes your point. If f is a pointer, then f->spam means to dereference the pointer f, then access the spam attribute of the result. If f is an instance of a user-defined class, rather than a raw pointer, it can redefine ->. But only to change the "dereference" part, not the "access the spam attribute" part. In other words, f->spam means something like (*(f.operator->())).spam and you can overload the operator->. Of course, under very restricted conditions, you can do something like this: #include <iostream> struct pstatus; struct status { int eggs, spam; } struct pstatus { status *p; status* operator->() { return reinterpret_cast<status*>(reinterpret_cast<char*>(p) - sizeof(int)); } }; pstatus frob() { return pstatus{new status{1, 2}}; } int main() { auto f = frob(); // eggs = 1, spam = 2 std::cout << f->spam; // accesses eggs, and prints 1 rather than 2 return 0; } Compile that with g++ or clang++ with -std=c++11 (we don't actually need any C++11 feature here, the code's just a bit shorter and simpler with auto, etc.), and it should compile without warnings, and print out 1 rather than 2 when you run it. The trick is that "access the spam attribute" actually means "access bytes 4-8 of the struct as an int", so if we have a pointer to 4 bytes before the start of the real struct, you get the int at bytes 0-4 of the real struct, which is the real eggs. (Actually, we should be using the difference between offsetof(spam) and offsetof(eggs), not sizeof(int), so it would be legal even with non-default alignment. But so many tiny things could turn this code into undefined behavior, even with that change, like just adding a virtual method to status, or referencing f->eggs in code that doesn't even run… so let's not worry about bulletproofing it.)