Search code examples
c++private

Why is public method in private struct accessible


Why does this code compile and lib::Foo::SecretStruct::SecretFunc can be accessed despite SecretStruct being private?

#include <iostream>
#include <memory>
#include <type_traits>

namespace lib {
template <typename T> struct Bar {};

struct Foo {
private:
  struct SecretStruct {
    static void SecretFunc() {
      std::cout << "super secret function is called\n";
    }
  };

public:
  using TBar = Bar<SecretStruct>;
};
} // namespace lib

template <typename T> struct GetSecretImpl {};
template <typename T>
struct GetSecretImpl<lib::Bar<T>> : std::type_identity<T> {};

int main() {
  using SecretT = typename GetSecretImpl<lib::Foo::TBar>::type;
  SecretT::SecretFunc();
}

Solution

  • Anything private that becomes a part of a public interface becomes visible to code outside.

    Consider the following code:

    #include <iostream>
    
    struct Outer
    {
    private:
        struct Inner { static void Foo() { std::cout << "Foo.\n"; } };
    
    public:
        static Inner GetInner();
    };
    
    int main()
    {
        decltype(Outer::GetInner()) inner;
        inner.Foo();
        return 0;
    }
    

    Since Inner is the return type of a public member function, it can be used by code outside Outer despite itself being a private struct.

    Your code is more involved but in principle does the same thing:

    public:
      using TBar = Bar<SecretStruct>;
    

    TBar is part of the public interface. SecretStruct is used by TBar, so it also becomes a part of the public interface.


    In C++, the access modifiers only control access to names. This means you cannot use the names Outer::Inner (in my example) or lib::Foo::SecretStruct directly from outside the scope they are declared in. In those cases, name lookup is performed first to identify the entity being accessed, then the found entity is checked with access rules.

    If there are others ways to access an entity indirectly without spelling out its partial or fully qualified name, access modifiers can't stop them.