Как скрыть данные от всех, кроме класса T
Я хочу, чтобы тип A передавал свои скрытые данные объекту типа T, но скрывал данные от всех остальных. Мой компилятор C++ это GCC 4.4, но это не должно иметь значения. Почему это не сработает?
#include <iostream>
template <class T> class A {
private:
int n1;
public:
friend class T;
A(const int n0 = 0) : n1(n0) {}
};
class B {
public:
int f(const A<B> a) const { return a.n1; }
B() {}
};
int main() {
const A<B> a(5);
const B b;
const int m = b.f(a);
std::cout << m << "\n";
return 0;
}
Между прочим, это работает хорошо, за исключением того, что не может скрыть данные:
#include <iostream>
template <class T> class A {
private:
int n1;
public:
int n() const { return n1; }
A(const int n0 = 0) : n1(n0) {}
};
class B {
public:
int f(const A<B> a) const { return a.n(); }
B() {}
};
int main() {
const A<B> a(5);
const B b;
const int m = b.f(a);
std::cout << m << "\n";
return 0;
}
Действительно ли C++ не позволяет указывать класс друга во время компиляции в качестве параметра шаблона? Почему бы и нет? Если нет, то какую альтернативную технику я должен использовать, чтобы скрыть данные? (Можно было бы предпочесть метод компиляции, если это возможно.)
В чем мое недоразумение, пожалуйста?
(Я вижу некоторые ответы на связанные вопросы здесь и здесь, но либо они не отвечают на мой конкретный вопрос, либо я не понимаю, что они так делают. В любом случае, возможно, я использую неправильную технику вообще. Хотя я по-прежнему заинтересован в почему класс T друга терпит неудачу, что я действительно хочу знать, так это как скрыть данные, будь то с другом или другими способами.)
Благодарю.
2 ответа
Я не знаю, что стоит за вашей ошибкой (см. Ответ Xeo), но я нашел обходной путь для C++03. Вместо того, чтобы делать T
друг, сделай один из T
член функции друга:
#include <iostream>
template <class T> class A {
private:
int n1;
public:
friend int T::getN1(const A& a) const;
A(const int n0 = 0) : n1(n0) {}
};
class B {
public:
int f(const A<B> a) const { return getN1(a); }
B() {}
private:
int getN1(const A<B>& a) const {return a.n1;}
};
class C {
public:
int f(const A<B> a) const { return getN1(a); }
C() {}
private:
// Error, n1 is a private member of A<B>
int getN1(const A<B>& a) const {return a.n1;}
};
int main() {
const A<B> a(5);
const B b;
const int m = b.f(a);
std::cout << m << "\n";
return 0;
}
В качестве альтернативы, вы можете сделать вложенный класс / структуру T другом для A. Это может быть более удобно, если есть несколько закрытых членов A, к которым вы хотите, чтобы T имел доступ.
#include <iostream>
template <class T> class A {
private:
int n1;
public:
friend class T::AccessToA;
A(const int n0 = 0) : n1(n0) {}
};
class B {
public:
int f(const A<B> a) const { return AccessToA::getN1(a); }
B() {};
private:
friend class A<B>;
struct AccessToA
{
static int getN1(const A<B>& a) {return a.n1;}
};
};
class C {
public:
int f(const A<B> a) const { return AccessToA::getN1(a); }
C() {};
private:
friend class A<C>;
struct AccessToA
{
// Error, n1 is a private member of A<B>
static int getN1(const A<B>& a) {return a.n1;}
};
};
int main() {
const A<B> a(5);
const B b;
const int m = b.f(a);
std::cout << m << "\n";
return 0;
}
Ваш компилятор просто слишком стар. C++11 позволяет вам объявить параметры шаблона друзьями.
§11.3 [class.friend] p3
Объявление друга, которое не объявляет функцию, имеет одну из следующих форм:
friend
elaborated-type-specifier
;
friend
simple-type-specifier
;
friend
typename-specifier
;
Если спецификатор типа в
friend
объявление обозначает (возможно, cv-квалифицированный) тип класса, этот класс объявляется какfriend
; в противном случае объявление друга игнорируется.
И он даже содержит пример параметра шаблона в качестве друга:
class C;
// [...]
template <typename T> class R {
friend T;
};
R<C> rc; // class C is a friend of R<C>
R<int> ri; // OK: "friend int;" is ignored
У C++03, к сожалению, нет способа сделать это, однако вы можете просто добавить в друзья одну свободную функцию и позволить ей действовать как "склеивающий" код, который берет данные из одного класса и передает их другому. Другим способом может быть шаблон пароля.