17

I made this simple class, which still is playing with my mind:

class A {
private:
    class B {};

public:
    B getB() {
        return B();
    };
};

As of C++03, this class compiles fine, but there is just no nice looking way to assign the result of getB() to an lvalue, in the sense that:

A::B b = A().getB();

Does not compile.

I got it by using an intermediate template, in this fashion:

template <typename T>
struct HideType {
    typedef T type;
};

HideType<A::B>::type b = A().getB();

But this looks just terrible, for this simple task of getting an A::B lvalue variable.

This is not true anymore as of C++11, or at least it is not with gcc. This code is still not valid:

A::B b = A().getB();

But this is valid:

auto b = A().getB();

Is there a loophole in the standard respect to this?

Samuel Navarro Lou
  • 1,168
  • 6
  • 17
  • 1
    Relevant: http://stackoverflow.com/questions/13532784/why-can-i-use-auto-on-a-private-type – OMGtechy Dec 13 '15 at 13:39
  • 1
    I cannot get [`HideType::type b = ...`](http://ideone.com/jwsXhG) to compile, could you show a MCVE where it happens? You might be interested in litb's [robber structure](http://stackoverflow.com/questions/15110526/allowing-access-to-private-members) for an alternative. – Matthieu M. Dec 13 '15 at 16:11

3 Answers3

11

From Standard, Clause 11 (Member access control):

A member of a class can be
— private; that is, its name can be used only by members and friends of the class in which it is declared.
— protected; that is, its name can be used only by members and friends of the class in which it is declared, by classes derived from that class, and by their friends (see 11.4).
— public; that is, its name can be used anywhere without access restriction.

So access control is applied to names.

In

auto b = A().getB();

you don't use private names, therefore it's legal, according to Standard

Victor Dyachenko
  • 1,363
  • 8
  • 18
1

The fact why A::B b = A().getB() doesn't work is because B is a private class member of A. If you make it public then your code will compile. It works with auto because auto just checks the type of the object assigned to it without needing to call the constructor of the object (just like declval). So it assigns b with the return type of getB present in class A. You can change your code in the following way too :

decltype( declval<A>().getB() ) b = A().getB();

( If declval is new to you then I must tell you that it would return an rvalue of the return type of the function (here getB whose return type is B) of a class (here A) without calling the constructor of the class ! (This should however be used with functions like decltype and sizeof only.) So it prevents the overhead of creating a class object and then using its function. )

Now according to me, I don't think this is a loophole in the standard rather I feel the loophole has been removed ! It's obvious from your own code, see the function getB. How difficult it is to instantiate an object of B because it's defined in private ! In such a case working with B becomes difficult. The idea behind this is that the name of the type (ie B) in the class A is inaccessible but the type is still usable which is why you can get an object of B. You can understand the use of auto if you understand the this template code :-

#include <iostream>
#include <type_traits>  // for std::is_same
using namespace std;
class A
{
    class B
    {};
    public:
    B getB()
    {
        return B();
    }
};
template<typename T>
void check (T b)
{
    cout<<boolalpha;
    is_same<decltype( declval<A>().getB() ), T> x;  // checks if T & B are of same type
    cout<<x.value<<'\n';
}
int main()
{
    A obj;
    check (obj.getB());
    return 0;
}

Output :-

true

As the template could identify B, hence auto too identifies B.

Anwesha
  • 728
  • 1
  • 7
  • 18
  • "*It works with `auto` because that's what `auto` is !!!! It will just check if the type assigned to it is valid or not*" This is all gibberish. – ildjarn Dec 13 '15 at 22:59
  • And `decltype` is C++11, just like `auto`, so what's the point in recommending it? – Sebastian Redl Dec 14 '15 at 09:35
  • Thats just an extra info to tell the OP that you can access object of `B` without even using `auto`. – Anwesha Dec 16 '15 at 17:24
0

It seems, there was such a defect in a draft of the Standard, but it had been corrected by WP 1170.

Probably, there is a compiler bug. The declaration auto b = A().getB(); involves template argument deduction for the auto type-specifier, so according to the C++11 Standard it should be ill-formed, because that type deduction fails.