2009-03-31 14 views
30

Beispiel:Einen Template-Parameter zu einem Freund machen?

template<class T> 
class Base { 
public: 
    Base(); 
    friend class T; 
}; 

Jetzt funktioniert das nicht ... Gibt es eine Möglichkeit, dies zu tun?

Ich bin eigentlich eine allgemeine Klasse Sealer wie diese zu machen versucht:

class ClassSealer { 
private: 
    friend class Sealed; 
    ClassSealer() {} 
}; 
class Sealed : private virtual ClassSealer 
{ 
    // ... 
}; 
class FailsToDerive : public Sealed 
{ 
    // Cannot be instantiated 
}; 

Ich fand dieses Beispiel auf dieser Seite irgendwo, aber ich kann es nicht finden ... (here)

Ich weiß, es gibt other ways davon zu tun, aber gerade jetzt bin ich neugierig, ob Sie tatsächlich so etwas tun können.

Antwort

32

Es ist explizit im Standard nicht erlaubt, auch wenn es einige Versionen von VisualStudio erlauben.

C++ Standard-7.1.5.3 Ausgearbeitt Typdeklarierer Absatz 2

3.4.4 describes how name lookup proceeds for the identifier in an elaborated-type-specifier. If the identifier resolves to a class-name or enum-name, the elaborated-type-specifier introduces it into the declaration the same way a simple-type-specifier introduces its type-name. If the identifier resolves to a typedef-name or a template type-parameter, the elaborated-type-specifier is ill-formed. [Note: this implies that, within a class template with a template type-parameter T, the declaration friend class T; is ill-formed. ]

I erkennt den Code, oben als ein Muster mit einer Klasse zu versiegeln (die Verlängerung nicht erlauben). Es gibt eine andere Lösung, die die Erweiterung nicht wirklich blockiert, die aber unbemerkt von der Klasse ausgeht. Wie in ADOBE Source Library gesehen:

namespace adobe { namespace implementation { 
template <class T> 
class final 
{ 
protected: 
    final() {} 
}; 
}} 
#define ADOBE_FINAL(X) private virtual adobe::implementation::final<T> 

mit der Nutzung:

class Sealed : ADOBE_FINAL(Sealed) 
{//... 
}; 

Während es Erweiterung ermöglicht es, wenn Sie es wirklich zwingen:

class SealBreaker : public Sealed, ADOBE_FINAL(Sealed) 
{ 
public: 
    SealBreaker() : adobe::implementation::final<Sealed>(), Sealed() {} 
}; 

Es wird verhindern, dass Benutzer versehentlich es tun.

EDIT:

Der bevorstehende C++ 11-Standard ermöglicht es Ihnen, eine Art Argument mit einer etwas anderen Syntax anfreunden:

gefunden
template <typename T> 
class A { 
    // friend class T; // still incorrect: elaborate type specifier 
    friend T;   // correct: simple specifier, note lack of "class" 
}; 
+0

... wieder erlaubt C++ 11 das Schlüsselwort "final", zum Beispiel: class X final {...} (oder Sie können einzelne virtuelle Funktionen endgültig machen). In jedem Fall habe ich den Code oben ("Freund T;") mit g ++ 4.8.4 _without_ the -std = C++ 11-Flag versucht und es kompiliert gut. –

3

Müssen Sie das wirklich tun? Wenn Sie verhindern möchten, dass jemand von Ihrer Klasse abgeleitet wird, fügen Sie einfach einen Kommentar hinzu und machen Sie den Destruktor nicht virtuell.

+0

:) Manchmal ist die beste technische Antwort überhaupt nicht technisch. –

+1

Sicher, aber es ist besser, wenn illegale Verwendung zur Kompilierzeit gekennzeichnet werden kann, nicht wahr? Es ist das gleiche Prinzip wie die Verwendung eines assert() anstelle eines Kommentars. Würden Sie zustimmen, dass assert() nützlich ist? –

+1

Manchmal kann der Destruktor nicht virtuell gemacht werden, da er möglicherweise eine Basisklasse hat, in der der Destruktor virtuell ist. –

16

ich einen einfachen Trick Template-Parameter zu deklarieren Freunde:

Allerdings weiß ich nicht, wie dies helfen könnte, eine alternative Version einer Klasse Sealer zu definieren .

+0

Irgendeine Idee, wie standardkonform das ist?Funktioniert super, danke für den Tipp! – zennehoy

+0

Sieht ziemlich Standard-konform zu mir aus, aber ich bin kein Standard-Guru. Schöner Fund! – onitake

+1

Nicht ganz ... Clang gibt mir unten Fehler: Fehler: erarbeitete Typ bezieht sich auf eine Typedef Freund Klasse TypeWrapper :: Typ; – Viren