4

Say that a base class A defines a protected member. A derived class B uses this member.

class A
{
public:
  A(int v) : value(v) { }

protected:
  int value;
};

class B : public A
{
public:
  B(int v) : A(v) { }
  void print() const;
  void compare_and_print(const A& other) const;
};

The function B::print just takes the value of the current member and prints it:

void B::print() const
{
  std::cout << "Value: " << value << "\n";
}

The other member function, B::compare_and_print, takes an instance of A, checks their values and prints the maximum of both:

void B::compare_and_print(const A& other) const
{
  auto max_value = std::max(value, other.value);
  std::cout << "Max value: " << max_value << "\n";
}

If other were an instance of the class B, this would be no problem. But the function would like to work with any kind of A instances. This does unfortunately not compile. This is what clang says about this:

test.cpp:27:42: error: 'value' is a protected member of 'A'
  auto max_value = std::max(value, other.value);
                                         ^
test.cpp:9:7: note: can only access this member on an object of type 'B'
  int value;
      ^
1 error generated.

This restriction sounds counter-intuitive to me. However, I'm not going to dispute the C++ standard about this (I'm nevertheless interested about the rationale behind this decision).

My problem is that in my project I really have this kind of use case: A derived class has a method that takes an instance of the base class and needs to access a protected member of the received object.

The easiest solution, the one I implemented currently, is to add a public member function to the base class, which returns the protected member. This solution does not fully satisfy me because I would like to avoid exporting the member this way.

How can I enable the usage of the base class' protected member by the derived class without exporting the member through the API?


EDIT: Some related questions are:

The answer I would like to have is the explanation of a design pattern to solve this problem without exposing the the protected member to the external code (where external means, code that is not part of the framework defining these classes).

It could be that such a pattern cannot exist, I acknowledge.

Spiros
  • 2,156
  • 2
  • 23
  • 42
  • can you please keep one of the sources only ? It makes it easier to follow – Raxvan Mar 28 '18 at 15:11
  • Yeah, I suppose the entire source in the bottom is redundant. I will remove it. – Spiros Mar 28 '18 at 15:12
  • Did you tried `A::value` [How to call a parent class function from derived class function?](https://stackoverflow.com/questions/357307/how-to-call-a-parent-class-function-from-derived-class-function). So make a getter of it. – Brighter side Mar 28 '18 at 15:15
  • I was confused about `protected` also, then a really good answer on SO let me understand it. Let me find it. – YSC Mar 28 '18 at 15:18
  • 1
    By making `friend`? – songyuanyao Mar 28 '18 at 15:27
  • 2
    @PeteBecker This is not a duplicate. This question asks "how" while that question asks "why". – xskxzr Mar 28 '18 at 15:34
  • 1
    In fact, the goal of my question was to have an answer from the object oriented perspective. The solutions I know (casting, friend declaration) are just technical tricks. If only such tricks allow me to do something, I'm probably doing it the wrong way. So, the actual question would be, what am I doing wrong? Why do I need to either sacrifice scope protection or use technical tricks? – Spiros Mar 28 '18 at 15:41
  • In the base class `A` declare the derived class `B` as a friend. This isn't a "technical trick" - the entire purpose of the `friend` declaration is to allow access to the private and protected members of other classes. – GrahamS Mar 28 '18 at 15:48
  • 1
    @GrahamS the entire purpose of protected members is to allow derived classes to use those members, which is what I need. The base class doesn't even have to be aware of which derived classes exist. By using `friend`, I need to edit the base class whenever a new derived class needs access to the protected member, which is a violation of the open-closed principle. It's not the clean solution I'm looking for. (BTW I might have some data in the base class I want to hide from the derived class too, by making it private) – Spiros Mar 28 '18 at 15:52

2 Answers2

6

There is actually a loophole using member pointers (no casting, no copying):

void B::compare_and_print(const A& other) const
{
  auto max_value = std::max(value, other.*(&B::value));
  std::cout << "Max value: " << max_value << "\n";
}
chtz
  • 17,329
  • 4
  • 26
  • 56
  • @YSC I think I found a safe option – chtz Mar 28 '18 at 23:16
  • Ok this is an upvote, I'm amazed! This felt so strange to me I asked [a specific question on SO as to why it is ok](https://stackoverflow.com/q/49550663/5470596). Check it out ;) – YSC Mar 29 '18 at 07:49
0

You can bypass protected by using a helper struct:

struct A_Helper : public A
{
    static int GetProtectedValue(const A & a)
    {
        return static_cast<const A_Helper&>(a).Value;
    }
};

You can get it by using it anywhere A_Helper::GetProtectedValue(a)

In your case, you could cast other to const B& (via static_cast or reinterpret_cast) but you don't know if instance of other is of type B. With that casted value people reading the code would presume that other is of type B and could insert code that causes reads/writes to "random" memory.

Consider that class B has another memeber value_B and other is of type C. Using static_cast<const B&>(other).value_B is Undefined Behavior.

Raxvan
  • 6,257
  • 2
  • 25
  • 46