2012-12-17 21 views
8

Natknąłem się na interesujący punkt, którego nie byłem w stanie wyjaśnić lub znaleźć wyjaśnienia. Rozważmy następującą definicję szablonu (skompilowany z mingw g ++ 4.6.2): ​​Specjalizacja niekompletna klasy szablonowej

template <typename T, typename S> 
class Foo 
{ 
public: 
    void f(){} 
    void g(){} 
}; 

Jeżeli chcemy, możemy w pełni specjalizuje się każdą pojedynczą funkcję użytkownika:

template <> 
void Foo<char,int>::f() {} 

Ale częściowa specjalizacja nie powiedzie się z " nieprawidłowe użycie niekompletnej typu 'class Foo < ...>'”błąd:

template <typename T, typename S> 
void Foo<T,S*>::f() 
{ 
} 

template <typename T> 
void Foo<T,int>::f() 
{ 
} 

I nie mogę zrozumieć dlaczego. Czy podjęto świadomą decyzję projektową, aby uniknąć problemu, którego nie mogę przewidzieć? Czy to przeoczenie?

Z góry dziękuję.

+0

Prawie duplikat: http://stackoverflow.com/questions/12335762/partial-specialization-of-member-function i http://stackoverflow.com/questions/165101/invalid-use-of-incomplete-type- Specjalizacja błędu z częściowym szablonem. – jogojapan

Odpowiedz

6

Pojęcie częściowej specjalizacji istnieje tylko dla szablonów klas (opisane przez §14.5.5) i szablonów członkowskich (tj członkowie klasy szablonu, które są same funkcje szablonów, opisane w §14.5.5.3/2). Nie istnieje dla zwykłych członków szablonów klas, ani nie istnieje dla szablonów funkcji – po prostu dlatego, że nie jest opisany przez Standard.

Teraz można argumentować, że podając definicję częściowej specjalizacji funkcji składowej, takich jak

template <typename T> 
void Foo<T,int>::f() 
{ } 

ty niejawnie określenia częściowej specjalizacji szablonu klasy: Foo<T,int>.To jednak jest wyraźnie wykluczone przez Standard:

(§14.5.5/2) Each class template partial specialization is a distinct template and definitions shall be provided for the members of a template partial specialization (14.5.5.3).

(§14.5.5.3/1) [...] The members of the class template partial specialization are unrelated to the members of the primary template. Class template partial specialization members that are used in a way that requires a definition shall be defined; the definitions of members of the primary template are never used as definitions for members of a class template partial specialization. [...]

To ostatnie oznacza, że ​​niemożliwe jest niejawnie określić częściową specjalizację po prostu podając definicję jednego z jej członków: Samo istnienie tego elementu nie wynika z definicji głównego szablonu, dlatego zdefiniowanie go jest równoznaczne z definiowaniem funkcji składowej, która nie była zadeklarowana jako, a to nie jest dozwolone (nawet w przypadku klas innych niż szablony).

Z drugiej strony, pojęcie wyraźnej specjalizacji (lub pełnej specjalizacji, jak to nazwać) istnieje dla funkcji składowych szablonów klas. To jest wyraźnie opisany przez Standard:

(§14.7.3/1) An explicit specialization of any of the following:
[...]
— member function of a class template
[...]
can be declared by a declaration introduced by template<>; [...]

§14.7.3/14 opisuje szczegóły:

(§14.7.3/14) A member or a member template of a class template may be explicitly specialized for a given implicit instantiation of the class template, even if the member or member template is defined in the class template definition. [...]

Stąd dla wyraźnych specjalizacji członków, konkretyzacji reszty prac klasy szablonu domyślnie – pochodzi z podstawowej definicji szablonu lub wszelkich częściowych specjalizacji, jeśli zostały zdefiniowane.

+0

Oto czego szukałem. Nie udało się odczytać odpowiednich części normy. Dzięki :) – StoryTeller

3

Myślę, że różnica jest taka, że ​​gdy zrobisz pierwszy (prawidłowy) wyraźnej specjalizacji f:

template <> 
void Foo<char,int>::f() {} 

Robicie niejawny konkretyzacji Foo<char,int>. Ale podczas próby częściowej specjalizacji z:

template <typename T> 
void Foo<T,int>::f() 
{ 
} 

kompilator musiałby instancję niejawnie Foo<T,int> przed robi specjalizację, ale nie może tego zrobić ze względu na T. I zawiedzie.

Można sprawdzić, że jest to sprawa z następującego kodu:

template <typename T, typename S> 
class Foo 
{ 
public: 
    void f(){} 
    void g(){} 
}; 


template <> 
void Foo<char,int>::f() //line 11 
{} 

template <> 
class Foo<char,int> //line 15 
{}; 

Z g++ daje błędy:

test.cpp:15:7: error: specialization of ‘Foo<char, int>’ after instantiation 
test.cpp:15:7: error: redefinition of ‘class Foo<char, int>’ 
test.cpp:2:7: error: previous definition of ‘class Foo<char, int>’ 

Z clang++ jest nieco jaśniejsze:

test.cpp:15:7: error: explicit specialization of 'Foo<char, int>' after instantiation 
class Foo<char,int> 
     ^~~~~~~~~~~~~ 
test.cpp:11:6: note: implicit instantiation first required here 
void Foo<char,int>::f() 
    ^
+0

Dzięki, to było wnikliwe :) – StoryTeller

5

Próbowałem znaleźć zwięzły cytat ze standardu, ale nie sądzę, że Jest to jeden. Faktem jest, że nie ma czegoś takiego jak częściowa specjalizacja funkcji szablonu (lub, jeśli o to chodzi, aliasu szablonu). Tylko szablony zajęć mogą mieć częściowe specjalizacje.

Zapomnijmy o szablonach na sekundę. W C++ istnieje duża różnica między nazwami klas i nazwami funkcji. Może istnieć tylko jedna definicja klasy w danym zakresie. (Możesz mieć różne deklaracje, ale wszystkie odnoszą się do Jedynej Prawdziwej Klasy.) Tak więc nazwa naprawdę identyfikuje klasę.

Z kolei nazwa funkcji jest rodzajem identyfikacji grupowej. Możesz zdefiniować dowolną liczbę funkcji w zakresie o dokładnie takiej samej nazwie. Kiedy używa się nazwy funkcji do wywoływania funkcji, kompilator musi dowiedzieć się, która funkcja naprawdę miała na myśli, patrząc na różne możliwości i dopasowując sygnaturę każdego z nich za pomocą dostarczonych argumentów. Nie ma związku między różnymi funkcjami, które mają wspólne imię; są całkowicie odrębnymi bytami.

Tak, nic wielkiego. Wiedziałeś o tym wszystkim, prawda? Ale teraz wróćmy do szablonów.

Nazwa klasy szablonowej jest nadal unikatowa.Chociaż możesz zdefiniować częściowe specjalizacje, musisz wyraźnie wyspecjalizować tę samą klasę szablonową. Mechanizm ten wygląda pozornie podobnie jak algorytm rozpoznawania nazw funkcji, o którym mowa powyżej, ale istnieją znaczne różnice - jednym z nich jest to, że w przeciwieństwie do prototypów funkcji nie można mieć dwóch szablonów klas w tym samym zakresie z różnymi rodzajami parametrów szablonu.

Z kolei funkcje szablonów nie wymagają definiowania unikalnych nazw. Templating nie zastępuje normalnego mechanizmu przeciążenia funkcji. Tak więc, gdy kompilator próbuje dowiedzieć się, co oznacza nazwa funkcji, musi uwzględnić wszystkie szablony i niesformatowane deklaracje dla tej nazwy funkcji, rozwiązać szablony z zestawem przyporządkowania parametrów szablonu (jeśli to możliwe), a następnie raz ma listę możliwych obiektów funkcji, wybierz najlepszy z normalną rozdzielczością przeciążenia.

To całkiem inny algorytm niż szablonowa rozdzielczość szablonów szablonów. Zamiast dopasowywać listę podanych argumentów szablonów do listy zadeklarowanych parametrów szablonów, czyli w jaki sposób rozpoznaje szablony klas, musi przyjmować każdą szablonową funkcję, która może pasować (ma co najmniej odpowiednią liczbę parametrów, na przykład) ; dedukuj parametry szablonu przez ujednolicenie dostarczonych argumentów za pomocą szablonu; a następnie dodaj specjalizację rozstrzygnięcia do zestawu przeciążeniowego dla kolejnej rundy rozdzielczości przeciążania.

Przypuszczam, że możliwe byłoby również dodanie do tego procesu częściowej specjalizacji, ale interakcje między częściową specjalizacją a przeciążeniem funkcji wydają mi się prawdopodobnie prowadzić do zachowania pseudo-magicznego. W tym przypadku nie było to konieczne, a więc nie ma takiego mechanizmu. (Możesz w pełni wyspecjalizować szablon funkcji, pełna specjalizacja oznacza, że ​​nie ma żadnych argumentów do wyprowadzenia szablonu, więc nie stanowi to problemu).

To jest miarka: nie możesz częściowo specjalizować funkcji szablonowej, ale istnieje nic Cię nie powstrzymuje przed zapewnieniem dowolnej liczby szablonów funkcji o tej samej nazwie. Wszystkie z nich będą rozpatrywane w rozdzielczości przeładowania, a najlepsza wygra, jak zwykle.

Zwykle jest to wystarczające do zaspokojenia potrzeb związanych z przeładowaniem. Powinieneś pomyśleć o szablonowych funkcjach dokładnie w ten sam sposób, w jaki myślisz o normalnych funkcjach: wymyślić sposób wybrania tego, który chcesz na podstawie dostarczonych argumentów. Jeśli uważasz, że naprawdę potrzebujesz dostarczyć parametry szablonu w wywołaniu funkcji, zamiast je wydedukować, po prostu uczyń funkcję (prawdopodobnie statyczną) członkiem klasy szablonowej i podaj argumenty szablonu dla klasy.

nadzieję, że pomoże ...

+0

To naprawdę pomaga, w rzeczywistości. Dzięki :) – StoryTeller

Powiązane problemy