Die speziellen Freundschaften von Templates

Modernes C++ Rainer Grimm  –  0 Kommentare

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.

Die speziellen Freundschaften von Templates

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 Klasse Derived Freundschaft gewährt, ist eine von Derived abgeleitete Klasse nicht automatisch ein Freund von Base.
  • Freundschaft ist nicht transitiv. Wenn die Klasse B mit der Klasse A befreundet ist und die Klasse C mit der Klasse B befreundet ist, ist die Klasse C nicht automatisch mit der Klasse A 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.

Die speziellen Freundschaften von Templates

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).

Die speziellen Freundschaften von Templates


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).

Die speziellen Freundschaften von Templates

Wie geht's weiter?

In meinem nächsten Beitrag schreibe ich über einen der anspruchsvollen Bereiche von Templates: dependent names.