2009-03-31 8 views
30

Exemple:Faire un paramètre de modèle un ami?

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

Maintenant, cela ne fonctionne pas ... Est-il possible de le faire?

Je suis en fait d'essayer de faire un scellant de classe générale comme celui-ci:

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

J'ai trouvé cet exemple sur ce site quelque part, mais je ne peux pas le trouver ... (here)

Je sais qu'il y a other ways de faire ceci mais juste maintenant je suis curieux si vous pouvez réellement faire quelque chose comme ceci.

Répondre

32

Il est explicitement interdit dans la norme, même si certaines versions de VisualStudio le permettent.

C++ standard 7.1.5.3 type élaboré spécificateurs, paragraphe 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 reconnaissent le code ci-dessus en tant que motif pour sceller (interdire l'extension de) une classe. Il y a une autre solution, qui ne bloque pas vraiment l'extension mais qui va s'incliner sans se rallier à la classe. Comme on le voit dans ADOBE Source Library:

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

avec l'utilisation:

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

Bien qu'il permet l'extension si vous forcez vraiment:

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

Il limitera les utilisateurs de le faire par erreur.

EDIT:

La prochaine norme C++ 11 ne vous permet de lier d'amitié avec un argument de type avec une syntaxe légèrement différente:

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

... encore une fois, C++ 11 permet le mot-clé "final", par exemple: class X final {...} (ou vous pouvez rendre les fonctions virtuelles individuelles définitives). Dans tous les cas, j'ai essayé le code ci-dessus ("friend T;") avec g ++ 4.8.4 _without_ le drapeau -std = C++ 11 et il compile bien. –

3

Avez-vous vraiment besoin de faire cela? Si vous voulez empêcher quelqu'un de dériver de votre classe, ajoutez simplement un commentaire et rendez le destructeur non-virtuel.

+0

:) Parfois, la meilleure réponse technique n'est pas du tout technique. –

+1

Bien sûr, mais c'est mieux si une utilisation illégale peut être signalée au moment de la compilation, n'est-ce pas? C'est le même principe que d'utiliser un assert() au lieu d'un commentaire - ne seriez-vous pas d'accord que assert() est utile? –

+1

Parfois, vous ne pouvez pas rendre le destructeur non virtuel, car il peut avoir une classe de base où le destructeur est virtuel. –

16

J'ai trouvé une astuce simple à déclarer des paramètres du modèle comme amis:

template < typename T> 
struct type_wrapper 
{ 
    typedef T type; 
}; 


template < typename T> class foo 
{ 
    friend class type_wrapper < T>::type 
}; // type_wrapper< T>::type == T 

Cependant, je ne sais pas comment cela pourrait aider à définir une version alternative d'un scellant de classe .

+0

Une idée de la conformité à cette norme?Fonctionne très bien, merci pour le pourboire! – zennehoy

+0

Cela me semble assez conforme à la norme, mais je ne suis pas un gourou standard. Belle trouvaille! – onitake

+1

Pas tout à fait ... clang me donne ci-dessous erreur: erreur: type élaboré se réfère à un typedef friend class TypeWrapper :: type; – Viren