2015-11-01 21 views
14

Si consideri il seguente codice:Un amico di A <T> può anche essere un amico di A <A<T>>?

#include <vector> 

template<typename T> class Container; 
template<typename T> Container<Container<T>> make_double_container(const std::vector<std::vector<T>>&); 

template<typename T> 
class Container { 
    std::vector<T> v; 
    friend Container<Container<T>> make_double_container<T>(const std::vector<std::vector<T>>&); 

public: 
    Container() {} 
    explicit Container(std::vector<T> v) : v(v) {} 
}; 

template<typename T> 
Container<Container<T>> make_double_container(const std::vector<std::vector<T>>& v) { 
    Container<Container<T>> c; 
    for(const auto& x : v) { 
     c.v.push_back(Container<T>(x)); 
    } 
    return c; 
} 

int main() { 
    std::vector<std::vector<int>> v{{1,2,3},{4,5,6}}; 
    auto c = make_double_container(v); 
    return 0; 
} 

Il compilatore mi dice che:

main.cpp: In instantiation of 'Container<Container<T> > make_double_container(const std::vector<std::vector<T> >&) [with T = int]': 
main.cpp:27:37: required from here 
main.cpp:8:20: error: 'std::vector<Container<int>, std::allocator<Container<int> > > Container<Container<int> >::v' is private 
    std::vector<T> v; 
        ^
main.cpp:20:9: error: within this context 
     c.v.push_back(Container<T>(x)); 

che io credo di essere corretto, perché make_double_container è amico di Container<T>, ma non di Container<Container<T>>. Come posso far funzionare make_double_container in questa situazione?

+1

La domanda è teoricamente interessante. Ma i vettori di vettori sono, in qualche modo, malvagi, quindi sconfigge soprattutto lo scopo. – Drop

risposta

10

Chiaramente, si può fare ogni specializzazione del make_double_container un amico:

template <typename U> 
friend Container<Container<U>> make_double_container(const std::vector<std::vector<U>>&); 

Se si desidera mantenere l'amicizia al minimo senza specializzazione parziale o simili, provare

template <typename> struct extract {using type=void;}; 
template <typename U> struct extract<Container<U>> {using type=U;}; 
friend Container<Container<typename extract<T>::type>> 
    make_double_container(const std::vector<std::vector<typename extract<T>::type>>&); 

Demo.

+0

Ho voluto precisamente evitare 'f ' diventare amico di 'A ' (primo snippet di codice). Penso che il tuo secondo snippet di codice dia un'amicizia "precisa" evitando il fastidio di scrivere molte "f" specializzate. Trucco intelligente, +1! –

3

Definire make_double_container come una funzione modello di S sembra farlo compilare e funzionare.

template<typename T> 
class Container { 
    std::vector<T> v; 

    template<class S> 
    friend Container<Container<S>> make_double_container(const std::vector<std::vector<S>>&); 

public: 
    Container() {} 
    explicit Container(std::vector<T> v) : v(v) {} 
}; 

http://coliru.stacked-crooked.com/a/bdc23a0451a2125b

quando il compilatore vede qualcosa nella struttura di:

template<class T> 
class X{}; 

quando si specifica cosa T è, instanciates la classe e sostituisce tutto con la typename T al specificato genere.

quando si scrive

Container<Container<T>> c; 

T è infatti Container<T>, e make_double_container turnes in

Container<Container<Container<T>>> make_double_container(const std::vector<std::vector<Container<T>>>&); 

e poi (all'interno principale) in

Container<Container<Container<int>>> make_double_container(const std::vector<std::vector<Container<int>>>&); 

modificando l'amicizia in :

template<class S> 
    friend Container<Container<S>> make_double_container(const std::vector<std::vector<S>>&); 

si forza il compilatore di capire cosa è S dal modello di Container<Container<T>>, e poi si capisce il giusto tipo di S, che è int

+0

La tua soluzione funzionerebbe, ma farebbe ad es. 'make_double_container ' un amico di 'Contenitore >'. –

+0

dovrebbe farlo. –

Problemi correlati