35

Consider the following snippet:

struct Base
{
  virtual ~Base() {}

  virtual void Foo() const = 0; // Public
};

class Child : public Base
{
  virtual void Foo() const {} // Private
};

int main()
{
  Child child;

  child.Foo(); // Won't work. Foo is private in this context.

  static_cast<Base&> (child).Foo(); // Okay. Foo is public in this context.
}

Is this legal C++? "This" being changing the virtual function's access mode in the derived class.

hlx236sk
  • 353
  • 1
  • 3
  • 4
  • 23
    @SauceMaster, That's not a very good indication of whether code is legal in C++. There's loads and loads of C++ code that'll compile and run, but is still invoking undefined behaviour. Change compilers, compiler flags, compiler version, and boom, it stops working. – Glen Jan 26 '10 at 17:25
  • Agreed with Glen. That's why I asked here. – hlx236sk Jan 26 '10 at 17:27
  • Apart from the one error "Foo is private in this context", g++ accepts it. – Thomas Jan 26 '10 at 17:27
  • 8
    Even if it is legal C++, remember that your maintenance programmers will hunt you down if you do this in production code. – Bill Jan 26 '10 at 17:40

4 Answers4

22

This is legal C++, §11.6/1 says:

Access is checked at the call point using the type of the expression used to denote the object for which the member function is called (B* in the example above). The access of the member function in the class in which it was defined (D in the example above) is in general not known.

As you noted, Child::Foo() is thus still accessible via the base class, which is in most cases undesired:

 Child* c = new Child;
 Base* b = c;
 c->Foo(); // doesn't work, Child::Foo() is private
 b->Foo(); // works, calls Child::Foo()

Basically, the declaration you refer to in the expression dictates the access mode - but virtual functions undermine that as another function then the named one may actually be invoked.

Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
20

Yes, changing the access mode in derived classes is legal.

This is similar in form but different in intent to the Non-Virtual Interface idiom. Some rationale is given here:

The point is that virtual functions exist to allow customization; unless they also need to be invoked directly from within derived classes' code, there's no need to ever make them anything but private.

As to why you would actually make something public in base but private in derived without private or protected inheritance is beyond me.

MSN
  • 53,214
  • 7
  • 75
  • 105
  • Nice. I was using this approach for a formatting facet but wasn't sure if this was legal. Thank you! – hlx236sk Jan 26 '10 at 17:45
  • 6
    This is quite contrary to the NVI: The base class' function is public+virtual, but in NVI it's private+virtual and the public function calling it is nonvirtual. – Johannes Schaub - litb Jan 26 '10 at 17:48
  • @litb, Ah, whups. Well, I fixed the text since I missed the base public virtual, derived private virtual. – MSN Jan 26 '10 at 18:39
  • It still doesn't address the actual question of the legality of changing access to members. – Georg Fritzsche Jan 26 '10 at 19:52
  • I thought that was apparent based on the articles I linked to. – MSN Jan 26 '10 at 20:53
  • @MSN: Could you expand on this please "As to why you would actually make something public in base but private in derived without private or protected inheritance is beyond me." – Graeme Jan 27 '14 at 23:56
5

It is perfectly legal C++. You are simply defining a new method in Child class.

Now does it do what you want it to do, that's an other question. I believe the access mode is not part of the method signature, which means that calling Base's Foo virtual method does eventually call Child's Foo method.

So here's the conclusion : it is legal c++ and it works the way you'd expect.

I am not taking into consideration the line child.Foo(); which can't work because there is no doubt it is trying to access Child's private Foo() method.

Benoît
  • 16,798
  • 8
  • 46
  • 66
4

It seems to compile and call the right method.

Remember that access specifiers are there to help a disciplined programmer, not to prevent all attempts to circumvent it at all costs.

In this particular case, Child has no business making the overridden virtual function private: isn't it supposed to implement the public interface of Base, so the "is-a" relationship holds? (If you didn't use public inheritance, which means "Child is a Base", your trick wouldn't work.)

UncleBens
  • 40,819
  • 6
  • 57
  • 90
  • 1
    "Remember that access specifiers are there to help a disciplined programmer, not to prevent all attempts to circumvent it at all costs." is a great point – austinmarton Apr 19 '17 at 03:54