7

Czy można w jakiś sposób uczynić częściową specyfikację szablonu przyjacielem? To znaczy. rozważyć masz następujące klasy SzablonSzablony w języku C++: Częściowe specyfikacje szablonów i klasy znajomych

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

teraz masz częściowych specjalizacji, na przykład, dla wskaźników

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

Co chcę osiągnąć jest to, że każda możliwa X<T*> jest klasa przyjaciel X<S> dla dowolnego S. To znaczy. X<A*> powinien być przyjacielem X<B>.

Oczywiście, myślałem o zwykłej deklaracji znajomego szablonu X:

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

Jednak to nie skompilować, g ++ mi mówi to:

test4.cpp: 34: 15: błąd : specjalizacja 'template<class T> class X' musi pojawić się w zakresie przestrzeni nazw

test4.cpp: 34: 21: błąd: częściowej specjalizacji 'X<S*>' ogłoszony 'przyjaciel'

Czy to w ogóle nie jest możliwe, czy istnieje jakieś obejście tego problemu?

Powodem dlaczego pytam jest to, że muszę konstruktora X<T*>, który tworzy tę klasę z dowolnego X<S> (S musi być podtypem T).

Kod wygląda następująco:

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

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

Teraz kompilator narzeka, oczywiście, że nie jest x.t visibile w konstruktorze, ponieważ jest prywatne. Dlatego potrzebuję częściowej klasy przyjaciół specjalności.

+1

Czy funkcja 'get' naprawdę nie wchodzi w grę? Wydaje mi się, że jest to dla mnie o wiele czystsze i omija szaleństwo przyjaciela. – pmr

+0

może to działać w tym przykładzie. Mogą jednak istnieć dane, które nie powinny być dostępne publicznie, a jedynie szablonowe specjalizacje. Pytanie brzmi, czy to zachowanie jest jakoś możliwe. – gexicide

Odpowiedz

3

W C++ możesz przyznać dostęp na poziomie wyższym niż private na czterech poziomach.

  • całkowicie public dostępu (patrz odpowiedź PMR)
  • dostępu w hierarchii dziedziczenia (protected, nieistotne tutaj)
  • do szablonu bazowego friend (zobacz tę odpowiedź)
  • do braku szablonu lub w pełni wyspecjalizowany friend (za słaby, aby rozwiązać Twój przypadek użycia)

Nie ma zakładu w środkowy sposób ween dwa ostatnie rodzaje przyjaźni.

Z § 14.5.4 normy C++ :.

Friend declarations shall not declare partial specializations.

Poniższa deklaracja pozwoli ci wdrożyć to, czego potrzebujesz. Daje ci wolną rękę, aby uzyskać dostęp do dowolnej specjalizacji twojego szablonu z jakiejkolwiek innej specjalizacji, ale wciąż tylko w granicach X. Jest nieco bardziej liberalny niż to, o co prosiłeś.

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

Możemy zdefiniować getter chroniony przez klucz określony w 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; 
} 

ten nadal ma pewne słabości. Każdy z użytkownikiem X<T*> może teraz używać get. Ale teraz jest to tak zaciemnione, że nikt nie jest w stanie tego zrozumieć. Wybrałbym zwykłą publiczną zdobyczę.

+1

Zasadniczo każdy z 'nullptr' może użyć' get'. Wygląda na to, że mamy tu konkurs na permisywność. –

+0

@JirkaHanika Możesz spróbować rozwiązać to z przeciążeniem 'null_ptr', ale nie sprawiłoby to żadnego lepszego. – pmr

+1

Tak, każdy z literałem "0" może nadal używać 'get'. –