Доступ к закрытому вложенному классу
Я сделал этот простой класс, который все еще играет со мной:
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 он должен быть плохо сформирован, потому что этот вывод не выполняется.