2015-11-01 14 views
14

Rozważmy następujący kod:Czy znajomy A <T> może być również przyjacielem 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; 
} 

Kompilator mi mówi, że:

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)); 

które uważam za słuszne, ponieważ make_double_container jest przyjacielem Container<T>, ale nie stanowi Container<Container<T>>. Jak mogę w tej sytuacji pracować make_double_container?

+1

Pytanie jest teoretycznie interesujące. Ale wektory wektorów są, w pewnym sensie, złe, więc pokonują cel głównie. – Drop

Odpowiedz

10

Oczywiście, można dołożyć wszelkich specjalizacji make_double_container się znajomego:

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

Jeśli chcesz zachować przyjaźń do minimum bez częściowej specjalizacji lub podobnego, spróbuj

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

Dokładnie chciałem uniknąć 'f ' stania się przyjacielem 'A ' (pierwszy fragment kodu). Myślę, że twój drugi fragment kodu daje "dokładną" przyjaźń, jednocześnie unikając kłopotów z pisaniem wielu specjalistycznych "f". Sprytna sztuczka, +1! –

3

Definiowanie make_double_container jako funkcji szablonu z S wydaje się sprawiać, że kompiluje się i działa.

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

gdy kompilator widzi coś w strukturze:

template<class T> 
class X{}; 

gdy podasz co T jest to instanciates klasę i zastępuje wszystko z TypeName T do określonego rodzaj.

kiedy piszesz

Container<Container<T>> c; 

T jest w rzeczywistości Container<T> i make_double_container Turnes język

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

a następnie (wewnątrz główny) do

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

zmieniając przyjaźń do :

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

zmusić kompilator, aby dowiedzieć się, co jest S od wzoru Container<Container<T>>, a następnie dane, odpowiedni rodzaj S, który jest int

+0

Twoje rozwiązanie zadziałałoby, ale spowodowałoby to np. 'make_double_container ' przyjaciel 'Container >'. –

+0

powinien to zrobić. –

Powiązane problemy