7

È possibile in qualche modo rendere una specifica di modello parziale una classe di amici? Cioè considera si ha la seguente classe templateModelli C++: Specifiche dei modelli parziali e classi di amici

template <class T> class X{ 
    T t; 
}; 

Ora avete specializzazioni parziali, per esempio, per i puntatori

template <class T> class X<T*>{ 
    T* t; 
}; 

Quello che voglio realizzare è che ogni possibile X<T*> è un amico classe di X<S> per QUALSIASI S. Cioè X<A*> dovrebbe essere un amico di X<B>.

Naturalmente, ho pensato a un normale amico dichiarazione di modello in X:

template <class T> class X{ 
    template <class S> friend class X<S*>; 
} 

Tuttavia, questo non si compila, g ++ mi dice questo:

test4.cpp: 34: 15: errore : la specializzazione di 'template<class T> class X' deve apparire in ambito namespace

test4.cpp: 34: 21: errore: la specializzazione parziale 'X<S*>' dichiarato 'amico'

Non è affatto possibile o c'è qualche soluzione?

Il motivo per cui chiedo è che ho bisogno di un costruttore in X<T*> che crea questa classe da un arbitrario X<S> (S deve essere un sottotipo di T).

Il codice simile a questo:

template <class T> class X<T*>{ 
    T* t; 

    template<class S> 
    X(X<S> x) : t(&(x.t)) {} //Error, x.t is private 
} 

Ora, il compilatore si lamenta, naturalmente, che x.t non è visibile nel costruttore dal momento che è privato. Questo è il motivo per cui ho bisogno di una classe di amici di specializzazione parziale.

+1

Una funzione 'get' è davvero fuori questione? Questo mi sembra molto più pulito ed evita tutte le follia degli amici del template. – pmr

+0

potrebbe funzionare in questo esempio, forse. Tuttavia, potrebbero esserci dati che non dovrebbero essere esposti al pubblico ma solo a specializzazioni di modelli. La domanda è se questo comportamento è possibile in qualche modo. – gexicide

risposta

3

In C++, è possibile concedere l'accesso oltre private su quattro livelli.

  • completamente public accesso (vedi risposta del PMR)
  • accesso all'interno di gerarchia di ereditarietà (protected, irrilevante qui)
  • a un modello di base friend (vedi questa risposta)
  • a un non-modello o completamente specializzata friend (troppo debole per risolvere il caso d'uso)

non v'è alcun centro t modo tra gli ultimi due tipi di amicizia.

Da §14.5.4 dello standard C++ :.

Friend declarations shall not declare partial specializations.

La seguente dichiarazione consente di implementare ciò che è necessario. Ti dà una mano libera per accedere a qualsiasi specializzazione del tuo modello da qualsiasi altra specializzazione, ma solo all'interno di X. È leggermente più permissivo di quello che hai chiesto.

template<class T> class X 
{ 
    template<class Any> friend class X; 
    public: 
     ... 
}; 
1

possiamo definire una getter protetto da una chiave definita in X.

#include <type_traits> 

template <class T> class X{ 
    T t; 
public: 
    struct Key { 
    template<typename S> 
    Key(const X<S>&) { 
     static_assert(std::is_pointer<S>::value, "Not a pointer"); 
    } 
    }; 

    const T& get(Key) const { return t; } 
    T& get(Key) { return t; } 
}; 

template <class T> class X<T*> { 
    T* t; 
public: 
    template<class S> 
    X(X<S>& x) : t(&(x.get(typename X<S>::Key(*this)))) {} 
}; 

int main() 
{ 
    X<int> x1; 
    X<int*> x2(x1); 
    return 0; 
} 

Questo ha ancora una certa debolezza. Tutti con uno X<T*> possono ora utilizzare get. Ma questo è così offuscato da ora, che nessuno sta facendo a rendersene conto. Sceglierei un semplice getter pubblico.

+1

Fondamentalmente, chiunque abbia un 'nullptr' può usare un' get'. Sembra che abbiamo una gara di permissività qui. –

+0

@JirkaHanika Puoi provare a risolverlo con un sovraccarico 'null_ptr', ma non migliorerebbe niente di tutto ciò. – pmr

+1

Sì, chiunque abbia un valore letterale '0' può ancora usare' get'. –