Die speziellen Freundschaften von Templates
Ein Freund hat uneingeschränkten Zugang zu den Mitgliedern einer Klasse. Deshalb sollte die Freundschaft mit Bedacht vergeben werden. In Bezug auf Templates verhält sich die Freundschaft besonders.
- Rainer Grimm
Ein Freund hat uneingeschränkten Zugang zu den Mitgliedern einer Klasse. Deshalb sollte die Freundschaft mit Bedacht vergeben werden. In Bezug auf Templates verhält sich die Freundschaft besonders.
Bevor ich ĂĽber die Regeln zu friend
für Templates schreibe, möchte ich die allgemeinen Regeln zur Freundschaft vorstellen.
- Die
friend
Deklaration kann an jeder beliebigen Stelle in der Klasse erfolgen. - Bei der
friend
Deklaration werden die Zugriffsrechte in der Klasse nicht berĂĽcksichtigt. - Freundschaft wird nicht vererbt. Wenn eine Klasse
Base
einer KlasseDerived
Freundschaft gewährt, ist eine vonDerived
abgeleitete Klasse nicht automatisch ein Freund vonBase
. - Freundschaft ist nicht transitiv. Wenn die Klasse
B
mit der KlasseA
befreundet ist und die KlasseC
mit der KlasseB
befreundet ist, ist die KlasseC
nicht automatisch mit der KlasseA
befreundet.
Eine Klasse oder ein Klassen-Template kann mit einer Klasse oder einem Klassen-Template, einer Funktion oder einem Funktions-Template oder einem Typ befreundet sein.
Allgemeine Freundschaft
Eine Klasse oder ein Klassen-Template kann jeder Instanz eines Klassen-Templates oder Funktions-Template Freundschaft gewähren.
// generalFriendship.cpp
#include <iostream>
template <typename T> // (1)
void myFriendFunction(T);
template <typename U> // (2)
class MyFriend;
class GrantingFriendshipAsClass {
template <typename U> friend void myFriendFunction(U);
template <typename U> friend class MyFriend;
std::string secret{"Secret from GrantingFriendshipAsClass."};
};
template <typename T>
class GrantingFriendshipAsClassTemplate{
template <typename U> friend void myFriendFunction(U);
template <typename U> friend class MyFriend;
std::string secret{"Secret from GrantingFriendshipAsClassTemplate."};
};
template <typename T> // (3)
void myFriendFunction(T){
GrantingFriendshipAsClass myFriend;
std::cout << myFriend.secret << '\n';
GrantingFriendshipAsClassTemplate<double> myFriend1;
std::cout << myFriend1.secret << '\n';
}
template <typename T> // (4)
class MyFriend{
public:
MyFriend(){
GrantingFriendshipAsClass myFriend;
std::cout << myFriend.secret << '\n';
GrantingFriendshipAsClassTemplate<T> myFriend1;
std::cout << myFriend1.secret << '\n';
}
};
int main(){
std::cout << '\n';
int a{2011};
myFriendFunction(a);
MyFriend<double> myFriend;
std::cout << '\n';
}
(1) und (2) deklarieren das Funktions-Template myFriendFunction
und das Klassen-Template MyFriend
. Das Funktions-Template myFriendFunction
wird in (3) und das Klassen-Template MyFriend
in (4) definiert. Die Klassen GrantingFriendshipAsClass
und GrantingFriendshipAsClassTemplate
gewähren dem Funktions-Template myFriendFunction
und dem Klassen-Template MyFriend
Freundschaft. Aufgrund der Freundschaft können beide Templates die private Variable secrete
der Klasse und des Klassen-Templates direkt aufrufen.
Bei dem Klassen-Template GrantingFriendShipAsClassTemplate
gibt es einen Fallstrick: Normalerweise heiĂźt der erste Typparameter eines Templates T
. Wer - wie im folgenden Codeschnipsel - denselben Typparameternamen fĂĽr das Klassen-Template und das Funktions-Template myFriendFunction
oder das Klassen-Template MyFriend
verwendet, erhält eine Fehlermeldung. Der Bezeichner T
von myFriendFunction
oder MyFriend
verdeckt den Bezeichner T
des Klassen-Templates GrantingFriendshipAsClassTemplate
.
Das folgende Codeschnipsel stellt den Fallstrick dar.
template <typename T>
class GrantingFriendshipAsClassTemplate{
template <typename T> friend void myFriendFunction(T);
template <typename T> friend class MyFriend;
std::string secret{"Secret from GrantingFriendshipAsClassTemplate."};
};
Besondere Freundschaft
Eine besondere Freundschaft ist eine Freundschaft, die vom Typ des Template-Parameter abhängt.
// specialFriendship.cpp
#include <iostream>
template <typename T> void myFriendFunction(T);
template <typename U> class MyFriend;
class GrantingFriendshipAsClass {
friend void myFriendFunction<>(int); // (1)
friend class MyFriend<int>; // (2)
private:
std::string secret{"Secret from GrantingFriendshipAsClass."};
};
template <typename T>
class GrantingFriendshipAsClassTemplate {
friend void myFriendFunction<>(int);
friend class MyFriend<int>;
friend class MyFriend<T>; // (3)
private:
std::string secret{"Secret from GrantingFriendshipAsClassTemplate."};
};
template <typename T>
void myFriendFunction(T) {
GrantingFriendshipAsClass myFriend;
std::cout << myFriend.secret << '\n'; // (4)
GrantingFriendshipAsClassTemplate<T> myFriend1;
std::cout << myFriend1.secret << '\n'; // (5)
}
template <typename T> // (6)
class MyFriend {
public:
MyFriend() {
GrantingFriendshipAsClass myFriend;
std::cout << myFriend.secret << '\n';
GrantingFriendshipAsClassTemplate<int> myFriendInt;
std::cout << myFriendInt.secret << '\n';
GrantingFriendshipAsClassTemplate<T> myFriendT;
std::cout << myFriendT.secret << '\n';
}
};
int main() {
std::cout << '\n';
int a{2011};
myFriendFunction(a);
MyFriend<int> myFriend;
std::cout << '\n';
}
Die Klasse GrantingFriendshipAsClass
gewährt Freundschaft für die vollständige Spezialisierung des Funktions-Template myFriendFunction
fĂĽr int
(1) und des Klassen-Template MyFriend
fĂĽr int (2). Dasselbe gilt fĂĽr das Klassen-Template GrantingFrandshipAsClassTemplate
. (3) ist besonders, weil sie der vollständigen Spezialisierung für MyFriend
Freundschaft gewährt, die denselben Typparameter hat wie das Klassen-Template GrantingFrandshipAsClassTemplate
. Folglich kann das Funktions-Template myFriendFunction
secret
der Klasse GrantingFriendshipAsClass
aufrufen, wenn myFriendFunctions
eine vollständige Spezialisierung für int
ist (4) oder GrantingFriendshipAsClassTemplate
den gleichen Typ wie myFriendFunction
besitzt (5). Die entsprechende Argumentation gilt fĂĽr das Klassen-Template MyFriend
(6).
Freund zu Typen
Ein Klassen-Template kann seine Freundschaft auch an einen Typparameter vergeben.
// typeFriendship.cpp
#include <iostream>
template <typename T>
class Bank {
std::string secret{"Secret from the bank."};
friend T;
};
class Account{
public:
Account() {
Bank<Account> bank;
std::cout << bank.secret << '\n'; // (1)
}
};
int main(){
std::cout << '\n';
Account acc;
std::cout << '\n';
}
Die Klasse Bank
gewährt ihrem Typparameter T
Freundschaft. Daher kann ein Account
auf das secret
der Bankinstanz zugreifen: Bank<Account>
(1).
Wie geht's weiter?
In meinem nächsten Beitrag schreibe ich über einen der anspruchsvollen Bereiche von Templates: dependent names. ()