Политика наследования и недоступность защищаемых членов

Кажется, что защищенный член из класса шаблонной политики недоступен, даже с иерархией классов, которая кажется правильной.

Например, со следующим фрагментом кода:

#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>', Это ошибка компилятора или есть проблема в коде?

Спасибо

6

Решение

Давайте на минутку забудем о шаблоне и посмотрим на это:

 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() недоступен).

6

Другие решения

Вы неправильно поняли значение защищенного доступа.

Защищенные члены могут вызываться производными классами. Но только на базовом объекте, содержащемся внутри самого класса.

Например, если я упрощаю проблему, используя:

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 с частным доступом.

9

Не отвлекайтесь на шаблон. Это не имеет ничего общего с ошибкой. Линия в main на что жалуется компилятор, создает объект типа B<int, a> и пытается получить доступ к защищенному члену. Это не законно, независимо от типа. Вы можете использовать только защищенных пользователей из внутри функция-член или функция-друг. Например:

struct S {
protected:
void f();
};

int main() {
S s;
s.f(); // error: attempts to call a protected member function
}
2

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

Объекты явно передаются как база Тип класса не может иметь доступ к своим защищенным членам в функциях-членах производного класса.

Другими словами, если у нас есть:

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.

2
По вопросам рекламы [email protected]