Кажется, что защищенный член из класса шаблонной политики недоступен, даже с иерархией классов, которая кажется правильной.
Например, со следующим фрагментом кода:
#include <iostream>
using namespace std;
template <class T>
class A {
protected:
T value;
T getValue() { return value; }
public:
A(T value) { this->value = value; }
};
template <class T, template <class U> class A>
class B : protected A<T> {
public:
B() : A<T>(0) { /* Fake value */ }
void print(A<T>& input) {
cout << input.getValue() << endl;
}
};
int main(int argc, char *argv[]) {
B<int, A> b;
A<int> a(42);
b.print(a);
}
Компилятор (clang в OS X, но gcc возвращает тот же тип ошибки) возвращает следующую ошибку:
Untitled.cpp:18:21: error: 'getValue' is a protected member of 'A<int>'
cout << input.getValue() << endl;
^
Untitled.cpp:25:5: note: in instantiation of member function 'B<int, A>::print' requested here
b.print(a);
^
Untitled.cpp:8:7: note: can only access this member on an object of type 'B<int, A>'
T getValue() { return value; }
^
1 error generated.
Странно то, что последнее замечание от компилятора полностью верно, но уже применяется, так как b
тип объекта 'B<int, A>'
, Это ошибка компилятора или есть проблема в коде?
Спасибо
Давайте на минутку забудем о шаблоне и посмотрим на это:
class A {
protected:
int value;
int getValue() { return value; }
public:
A(int value) { this->value = value; }
};
class B : protected A {
public:
B() : A(0) { /* Fake value */ }
void print(A& input) {
cout << input.getValue() << endl;
}
};
print()
Реализация метода неверна, потому что вы не можете получить доступ к непубличному участнику A
внутри B
, И вот почему: изнутри B
, вы можете получить доступ только к закрытым членам B
, Эти члены могут быть или унаследованы или нет — это не имеет значения.
С другой стороны, A& input
не может быть ссылкой на экземпляр B
, Это может быть ссылка на другой подкласс (который может иметь getValue()
недоступен).
Вы неправильно поняли значение защищенного доступа.
Защищенные члены могут вызываться производными классами. Но только на базовом объекте, содержащемся внутри самого класса.
Например, если я упрощаю проблему, используя:
class A {
protected:
void getValue(){}
};
class B : protected A
{
public:
void print(A& input)
{
input.getValue(); //Invallid
}
};
getValue нельзя вызывать для объекта «A», кроме объекта «A» внутри самого класса.
Это, например, действительно.
void print()
{
getValue(); //Valid, calling the base class getValue()
}
Как указывают Дан Ниссенбаум и Шакуров. Однако это также верно:
void print(B& input)
{
input.getValue();
}
Это потому, что мы явно говорим, что input является объектом B. И компилятор знает, что все эти объекты B имеют защищенный доступ к getValue. В случае, когда мы передаем&объект может также быть типом C, который может быть получен из A с частным доступом.
Не отвлекайтесь на шаблон. Это не имеет ничего общего с ошибкой. Линия в main
на что жалуется компилятор, создает объект типа B<int, a>
и пытается получить доступ к защищенному члену. Это не законно, независимо от типа. Вы можете использовать только защищенных пользователей из внутри функция-член или функция-друг. Например:
struct S {
protected:
void f();
};
int main() {
S s;
s.f(); // error: attempts to call a protected member function
}
Функции-члены производного класса имеют доступ к защищенным членам базового класса в любом объекте его типа, который передается в качестве аргумента. до тех пор, пока явно объявленный класс объекта, переданного в качестве аргумента, является классом производного класса (или дальнейший производный класс).
Объекты явно передаются как база Тип класса не может иметь доступ к своим защищенным членам в функциях-членах производного класса.
Другими словами, если у нас есть:
class A
{
protected:
int x;
}
class B : public A
{
void foo(B b)
{
b.x; // allowed because 'b' is explicitly declared as an object of class B
}
void goo(A a)
{
a.x; // error because 'a' is explicitly declared as having *base* class type
}
};
…тогда линия a.x
не допускается, потому что явный тип аргумента A
, но правило для защищенного доступа применяется только к объектам, явно определенным как так же класс как класс, пытающийся получить доступ к члену. (… или класс, производный от него; т.е. если class C
происходит от B
затем передать объект, явно объявленный как объект класса C
также будет иметь x
доступны внутри B
функции-члены.)
Причину этого дает Шакуров, когда пишет (перефразируя)
& вход может не быть ссылкой на экземпляр B. Это может быть
ссылка на другой подкласс (который вполне может иметь getValue ()
недоступен)
Отличное объяснение этого ответа также приводится здесь: доступ к защищенному члену базового класса в другом подклассе.
Интересно, я полагаю, что это происходит из стандарта C ++ здесь:
11.4 Защищенный доступ к элементу [class.protected] 1 Дополнительная проверка доступа, помимо описанной ранее в разделе 11, применяется, когда
Нестатический член данных или функция нестатического члена является защищенным
член его класса именования (11.2) 115 Как описано ранее, доступ к
защищенный участник предоставлен, потому что ссылка встречается у друга
или член некоторого класса C. Если доступ заключается в формировании указателя на
член (5.3.1), спецификатор вложенного имени должен обозначать C или класс
происходит от C. Все другие обращения включают (возможно, неявный)
выражение объекта (5.2.5). В этом случае класс объекта
выражение должно быть C или класс, производный от C.