2017-09-14 7 views
6

J'ai trouvé "comment définir une fonction modèle d'ami d'une classe de modèle en dehors de sa déclaration" (SO/cppreference), mais comment faire si nous ajoutons une autre classe interne non modèle dans le mélange?Comment définir une fonction friend déclarée dans une classe non template interne à une classe template en dehors des deux classes?

I.e. comment (externe) définissent operator<< déclaré dans class Internal de l'exemple suivant:

#include <iostream> 

template <typename T> 
class External { 
public: 
    explicit External(T initial) : value{initial} {} 
    class Internal { 
    public: 
     Internal(const External& e) : internal_value{e.value} {} 

    private:   
     friend std::ostream& operator<<(std::ostream& os, const Internal& i); 
     // ^^^ this one 
     /* body 
     { 
      return os << i.internal_value; 
     } 
     */ 

     T internal_value; 
    }; 

    friend std::ostream& operator<<(std::ostream& os, const External& e) 
    { 
     return os << Internal{e}; 
    } 
private: 
    T value; 
}; 

int main() 
{ 
    std::cout << External<int>{5}; 
} 
+0

@StoryTeller quelles sont les règles de recherche? –

+0

@RichardHodges - Vous savez quoi, je n'en suis plus si sûr. Je pourrais être déroutant mes règles de recherche. C'est seulement la fonction d'ami en ligne de l'OP qui est seulement trouvée par ADL. – StoryTeller

+3

Qui a voté pour que cela soit trop large ...? –

Répondre

6

est ici la question. Malgré le fait que External est un modèle et Internal est un type dépendant. La fonction ami n'est pas modélisée elle-même. Aussi étrange que cela puisse paraître, cela ne dépend pas du paramètre template. Lorsque vous le définissez en ligne, chaque spécialisation du modèle crée également la fonction amie associée. Mais quand ce n'est pas en ligne, vous devez fournir la définition de l'opérateur pour chaque spécialisation explicitement. Et vous ne pouvez pas le faire avec un modèle, car la fonction n'est pas un modèle.

Donc, si vous ajoutez ceci après la déclaration de modèle:

std::ostream& operator<<(std::ostream& os, External<int>::Internal const& i) 
{ 
    return os << i.internal_value; 
} 

Il construira. Et comme vous pouvez le voir, seuls les types concrets sont utilisés dans la fonction.

Évidemment, ce n'est pas vraiment une solution. Toute personne sensée voudrait des spécialisations de External pour générer la définition de l'ami. La façon d'y parvenir de manière maintenable est de garder la définition de l'opérateur en ligne, mais au lieu de faire le travail là-bas, déléguer à une fonction membre (qui est en fonction du paramètre du modèle):

class Internal { 
public: 
    Internal(const External& e) : internal_value{e.value} {} 

private: 
    std::ostream& print(std::ostream&) const; 
    friend std::ostream& operator<<(std::ostream& os, Internal const& i) 
    { 
     return i.print(os); // Short and sweet on account of being inline 
    } 

    T internal_value; 
}; 

//.... 

template<typename T> 
std::ostream& External<T>::Internal::print(std::ostream& os) const { 
    // Provided outside of the class definition, can be as verbose as you like 
    return os << internal_value; 
} 
+2

hah! On dirait que vous avez à la réponse des moments avant moi. Votre réponse est plus complète. Je vais supprimer le mien. –