9

I would like to know why the following code does not compile:

class base {
protected:
  typedef void (base::*function_type)() const;
  void function_impl() const {} // error: ‘void base::function_impl() const’ is protected
};

class derived: public base {
public:
  operator function_type() const {
    return boolean_test() == true ? &base::function_impl : 0; // error: within this context
  }

protected:
  virtual bool boolean_test() const = 0;
  virtual ~derived() {}
};

int main(int argc, char* argv[]) {
}

g++ output:

~/protected_test$ g++ src/protected_test.cpp
src/protected_test.cpp: In member function ‘derived::operator base::function_type() const’:
src/protected_test.cpp:4:8: error: ‘void base::function_impl() const’ is protected
src/protected_test.cpp:10:44: error: within this context

This code was adapted from here and I see no one complaining about that at the discussion forum. Also, I'm using g++ 4.7.2 and the same code compiles and links fine with egcs-2.91.66.

freitass
  • 6,542
  • 5
  • 40
  • 44

1 Answers1

10

The specification of protected access states that pointer to members have to be formed either through the derived type (i.e. derived::...) or a type inherited from it. You can't name function_impl directly through base.

That means that in your case you have to do it as

operator function_type() const {
  return boolean_test() == true ? &derived::function_impl : 0;
}

Note that even if you use &derived::function_impl expression to obtain the address, the type of the result is still void (base::*function_type)() const, since the name function_impl in this case resolves to the function of base class.

If it used to compile in some specific compiler (or some specific version of it), it simply means that that compiler allowed the error to slip through, which is what probably explains the code at the link.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 1
    I don't agree with the type of the result -- the behavior you describe is not type-safe (I'm not sure if the mistake is yours or in the C++ Standard). This creates a loophole that allows `Derived` member functions to indirectly call `o.function_impl()` even when `dynamic_cast(o)`, which is not supposed to be legal. – Ben Voigt Jun 06 '13 at 20:23
  • Ok, it looks like the example in the C++ Standard is broken. – Ben Voigt Jun 06 '13 at 20:26
  • @Ben Voigt: Can you elaborate a bit more on that, please? – AnT stands with Russia Jun 06 '13 at 20:27
  • Compare http://ideone.com/pEXk9W to http://ideone.com/zJX5U6 It definitely is the Standard that is broken, and not your interpretation in this answer. – Ben Voigt Jun 06 '13 at 20:40
  • For further discussion: http://stackoverflow.com/questions/16971897/in-c11-protected-means-public – Ben Voigt Jun 06 '13 at 20:50
  • @Ben Voigt: Oh, this is the "protected hack" that I have seen mentioned several times before. Yes, it does indeed breaks incapsulation and works around access protection. I though you meant something else when you said it was "not type-safe". It actually looks type-safe to me, since the access itself it valid, even if you obtained it by working around access protection. – AnT stands with Russia Jun 06 '13 at 20:50
  • @AndreyT: Breaking access protection violates type safety (you can break invariants). – Ben Voigt Jun 06 '13 at 20:52
  • @Ben Voigt: Well, I meant a lower-level concept of type-safety. I.e. type safety is be broken when one can access an object of type `T` as an object of type `U`, where there's no IS-A relationship from `T` to `U`. – AnT stands with Russia Jun 06 '13 at 21:07