mfnx
mfnx

Reputation: 3018

Shouldn't access to private types be prohibited?

Consider this code:

class A
{
    struct B {};
    std::vector<B> _vec;

public:
    const std::vector<B>& get() const { return _vec; }
};

Note that B is private within class A. The above code compiles, but when calling get() from outside class A, it does not:

const std::vector<A::B>& vec = get(); // does not compile

Indeed, A::B is private. However, from C++11 on, you could simply do this:

const auto& vec = get();

which works perfectly.

For the same reason as above, you cannot do the following:

A::B obj; 

But, since there is a public getter, you can deduce the type from that getter function. In this particular case, one could do:

using B = std::decay<decltype(C{}.get())>::type::value_type;
B obj;

Questions

I'm not sure how to formulate my questions. It seems strange to me that, from C++11 on (and not before), we actually can instantiate A::B being the latter private. And even more, I think it's strange we can call const auto& get(). Is there any explanation for this? Wouldn't it be better not be allowed doing this? Should I declare A::B public? I feel like it doesn't make sense to declare it private if you need a getter function like the one in the above code.

Upvotes: 5

Views: 196

Answers (2)

Nicol Bolas
Nicol Bolas

Reputation: 473192

It seems strange to me that, from C++11 on (and not before), we actually can instantiate A::B being the latter private.

You could do it in C++98/03 just fine, through template argument deduction:

template<typename T>
void temp_func(const T &t)
{
...
T u = t;
}

temp_func(a.get()); //Will use the private type.

You can even use T inside of temp_func.

Making a type private was never a guarantee of making the type inaccessible from the outside. Private has always refer to the accessibility of the name, not of the construct behind the name. If you want a type to only be used within a scope, then that typename cannot be part of any non-private interfaces. And this has always been the case.

Should I declare A::B public?

That's up to you. However, if you expose a public user interface, you are making a statement that the user can, you know, use the interface. In order for the user to know how to use a vector<A::B>, they have to know how A::B behaves. So they have to use its definition. Even though it's private.

So if a user has to know about a type and about how a type behaves... is it really "private"?

Upvotes: 3

Guillaume Racicot
Guillaume Racicot

Reputation: 41750

When you mark something private, it does not mean unaccessible. It simply mean that the declaration cannot be accessed from the outside.

For example, this will work:

class A {
    struct B {};

public:
    using C = B;
};

auto main() -> int {
    auto c = A::C{}; 
}

Here I access B, because I used the public alias to it.

Same thing for private members:

class A {
    int i;

public:
    auto the_i() -> int A::* {
        return &A::i;
    }
};

auto main() -> int {
    auto a = A{};
    auto member = a.the_i();

    // Whoa! I access the member directly! Shouldn't this prohibited?
    a.*member = 9;
}

The answer is no. The declaration is private, but can still be accessed by other means.

Same thing with member types, expose it in the public interface and users will be able to use it.

If you don't want users to use the private type, simply don't expose it in the public interface.

Upvotes: 2

Related Questions