Доступ к закрытому вложенному классу

Я сделал этот простой класс, который все еще играет со мной:

class A {
private:
    class B {};

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

Как и в С++ 03, этот класс компилируется отлично, но просто нет красивого способа присвоить результат getB() lvalue в том смысле, что:

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

Не компилируется.

Я получил его с помощью промежуточного шаблона следующим образом:

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

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

Но это выглядит просто ужасно, для этой простой задачи получения переменной A:: B lvalue.

Это больше не так, как на С++ 11, или, по крайней мере, это не с gcc. Этот код все еще недействителен:

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

Но этот имеет значение:

auto b = A().getB();

Есть ли лазейка в стандартном отношении к этому?

Ответы

Ответ 1

От стандарта, раздел 11 (Контроль доступа элемента):

Член класса может быть - частный; то есть его имя может использоваться только членами и друзьями класса, в котором он объявлен.
- защищенный; то есть его имя может использоваться только членами и друзьями класса, в котором он находится объявлены классами, полученными из этого класса, и их друзьями (см. 11.4).
- общественность; то есть его имя можно использовать в любом месте без ограничения доступа.

Таким образом, управление доступом применяется к именам.

В

auto b = A().getB();

вы не используете частные имена, поэтому он легален, в соответствии со стандартом

Ответ 2

Тот факт, что A::B b = A().getB() не работает, состоит в том, что B является частным членом класса A. Если вы сделаете это общедоступным, тогда ваш код будет скомпилирован. Он работает с auto, потому что auto просто проверяет тип назначенного ему объекта, не требуя вызова конструктора объекта (точно так же, как declval). Поэтому он присваивает B возвращаемый тип getB, присутствующий в классе A. Вы также можете изменить свой код следующим образом:

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

(Если declval является новым для вас, то я должен сказать вам, что он вернет rvalue возвращаемого типа функции (здесь getB, тип возврата которого B) класса (здесь A), не вызывая конструктор класса! (Однако это следует использовать только с функциями, такими как decltype и sizeof.) Таким образом, он предотвращает накладные расходы на создание объекта класса, а затем использует его функцию.)

Теперь, по моему мнению, я не думаю, что это лазейка в стандарте, но я чувствую, что лазейка была удалена! Это очевидно из вашего собственного кода, см. Функцию getB. Как сложно создать экземпляр объекта B, потому что он определен частным! В таком случае работа с B становится затруднительной. Идея этого заключается в том, что имя типа (т.е. B) в классе A недоступно, но тип все еще можно использовать, поэтому вы можете получить объект B. Вы можете понять использование auto, если вы понимаете этот код шаблона: -

#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;
}

Выход: -

true

Поскольку template мог идентифицировать B, поэтому auto тоже идентифицирует B.

Ответ 3

Кажется, в проекте стандарта был такой дефект, но он был исправлен WP 1170.

Вероятно, есть ошибка компилятора. Объявление auto b = A().getB(); включает в себя вывод аргумента шаблона для спецификатора типа auto, поэтому в соответствии со стандартом С++ 11 он должен быть плохо сформирован, потому что этот вывод не выполняется.