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();
}
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.