2015-10-10 23 views
7

Questo funziona:Come fornire la definizione di un operatore amico in una classe annidata di una classe template?

template<class Tim> 
struct Bob 
{ 
    struct Dave 
    { 
     Tim t{}; 
     friend bool operator < (const Dave& a, const Dave& b) 
     { 
      return a.t < b.t; 
     } 
    } d; 
}; 

Questo non funziona:

template<class Tim> 
struct Bob 
{ 
    struct Dave 
    { 
     Tim t{}; 
     friend bool operator < (const Dave& a, const Dave& b); 
    } d; 
}; 

template<class Tim> 
bool operator < (const typename Bob<Tim>::Dave& a, const typename Bob<Tim>::Dave& b) 
{ 
    return a.t < b.t; 
} 

Quando provo ad usarlo in una mappa, ad esempio, ricevo errori del linker:

1>ConsoleApplication1.obj : error LNK2019: unresolved external symbol "bool __cdecl operator<(struct Bob<int>::Dave const &,struct Bob<int>::Dave const &)" ([email protected][email protected][email protected]@@[email protected]) referenced in function "public: bool __thiscall std::less<struct Bob<int>::Dave>::operator()(struct Bob<int>::Dave const &,struct Bob<int>::Dave const &)const " ([email protected]@[email protected]@@@[email protected]@[email protected][email protected]@@[email protected]) 

.

int main() 
{ 
    std::map<Bob<int>::Dave, int> v; 
    v[{}]; 
} 

Come posso definire questo operatore correttamente di fuori la classe?

+0

Se si attivano avvisi, si ottiene: 'avviso: dichiarazione amico 'bool operatore <(const Bob :: Dave &, const Bob :: Dave &)' dichiara una funzione non modello '(e un suggerimento su cosa fare su di esso) - così l'amico 'operatore <' dichiari, e il 'template <...> operator <' che definisci più in basso non sono la stessa cosa. – melak47

+0

@ melak47 VS2015 livello di avviso 4, non ricevo alcun avviso. –

+0

Siamo spiacenti, guarda l'output di gcc: http://coliru.stacked-crooked.com/a/bb58214d285f031f – melak47

risposta

4

Normalmente si farebbe una cosa simile dichiarando in avanti la classe template e la funzione friend e fornendo quindi una specializzazione all'interno della definizione della classe. Tuttavia, in questo caso non è così facile - avere un tipo dipendente pone la classe Tim in un contesto non dedotto, quindi la deduzione fallirà. Tuttavia, c'è un modo intorno ad esso:

#include <iostream> 
#include <type_traits> 
#include <map> 

template<class T> 
struct Bob; 

template<typename T, typename> 
bool operator < (const T& a, const T& b); 

struct DaveTag {}; 

template<class Tim> 
struct Bob 
{ 
    struct Dave : DaveTag 
    { 
     Tim t{}; 


     friend bool operator < <Bob<Tim>::Dave, void>(const typename Bob<Tim>::Dave& a, const typename Bob<Tim>::Dave& b); 
    } d; 
}; 

template<typename T, typename = typename std::enable_if<std::is_base_of<DaveTag, T>::value>::type> 
bool operator < (const T& a, const T& b) 
{ 
    return a.t < b.t; 
} 

struct X { 
    double t; 
}; 

int main() 
{ 
    std::map<Bob<int>::Dave, int> v; 
    v[{}]; 

    // This won't work 
    // X x, y; 
    //bool b = x < y; 

} 

In sostanza, quello che ho fatto qui è lasciare che il compilatore dedurre la piena Bob<Tim>::Dave come parametro modello per operator<. Tuttavia, chiaramente una semplice definizione consentirebbe di dedurre qualsiasi tipo per T potenzialmente portando ad alcuni problemi di difficile comprensione. Per evitarlo ho aggiunto una piccola classe di tag DaveTag che consente di impedire le istanze del nostro generico operator< per qualsiasi cosa eccetto Dave.

+0

Apprezzo il tuo sforzo qui! –

+0

@NeilKirk Nessun problema! Mi stavo divertendo anch'io :) – Rostislav

+0

Mi è venuta una soluzione che è abbastanza buona per la mia situazione e un po 'più semplice :) –

0

Ecco una soluzione soddisfacente. Non volevo implementare l'operatore nella classe per vari motivi (definizioni non disponibile, a quel punto, un po 'esplicito modello di istanza succedendo qui), ma posso vivere con il seguente:

struct Bob 
{ 
    struct Dave 
    { 
     Tim t{}; 

     static bool LessThan(const Dave& a, const Dave& b); 

     friend bool operator < (const Dave& a, const Dave& b) 
     { 
      return LessThan(a, b); 
     } 
    } d; 
}; 

Ora la funzione statica LessThan può essere implementato fuori dalla classe nel modo normale.

Problemi correlati