ofo
ofo

Reputation: 1342

Casting an object to a derived class to access protected members of parent class

I'm trying to access a protected member of a class defined and implemented by an external library. I'd like to achieve that without copying or moving the instance if possible.

Here is a simplified example. Let's say this is the code from the external library:

// some_external_library.h

class Node {
    public:
        static Node Create();
    protected:
        Node(int val) : val_(val) {}
        int val_;
};

This is what I am trying to do:

// my_code.cpp

#include "some_external_library.h"

Node node = Node::Create();
int val = node.val_; // <-- This is invalid since val_ is protected.

Node class is part of some external library that I link to my program. So, I'd like to avoid modifying Node class by adding a public function or friend declaration for accessing val_. Also, I'd like to avoid creating a copy of node object or moving from it, so I'd like to avoid creating a derived class moving/copying the Node instance just to access a member field. Is it possible?

I came up with a possible solution, which worked fine for the minimal example:

// my_code.cpp

#include "some_external_library.h"
class NodeAccessor : public Node {
    public:
        int val() const { return val_; }
};

Node node = Node::Create();
int val = static_cast<const NodeAccessor&>(node).val();

However, I'm not sure if this is valid since node isn't an instance of NodeAccessor. Is this standard compliant? Would it cause any problems (e.g. optimizing away of val_ during compilation of the external library)?

Upvotes: 1

Views: 406

Answers (2)

Jarod42
Jarod42

Reputation: 218323

What you do is UB.

There is one magical way to access private/protected member in C++:

The ISO C++ standard specifies that there is no access check in case of explicit template instantiations, and following code abuse of that:

template <typename Tag>
struct result
{
  using type = typename Tag::type;
  static type ptr;
};

template <typename Tag> typename result<Tag>::type result<Tag>::ptr;

template<typename Tag, typename Tag::type p>
struct rob : result<Tag> {
  /* fill it ... */
  struct filler {
    filler() { result<Tag>::ptr = p; }
  };
  static filler filler_obj;
};

template <typename Tag, typename Tag::type p>
typename rob<Tag, p>::filler rob<Tag, p>::filler_obj;

And then

struct NodeVal { using type = int Node::*; };
template class rob<NodeVal, &Node::val_>;

and finally:

int main() {
  Node node = /**/;
  (node.*result<NodeVal>::ptr);
}

Demo

Upvotes: 1

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385405

Yes, that can and will and should cause problems.

You are casting a thing to a type, then the thing is not of that type.

All manner of antics can ensue as a result.

There is simply no way to do what you're trying to do, and that's a good thing! Access protections are there for a reason. Work with them, not against them.

Upvotes: 0

Related Questions