2010-05-12 16 views
6

DlaczegoJak zaprzyjaźnić się z konstruktorem klasy szablonowej?

class A; 
template<typename T> class B 
{ 
private: 
    A* a; 

public: 
    B(); 
}; 


class A : public B<int> 
{ 
private:  
    friend B<int>::B<int>(); 
    int x; 
}; 


template<typename T> 
B<T>::B() 
{ 
    a = new A; 
    a->x = 5; 
} 

int main() { return 0; } 

wynik w

../src/main.cpp:15: error: invalid use of constructor as a template
../src/main.cpp:15: note: use ‘B::B’ instead of ‘B::class B’ to name the constructor in a qualified name

jeszcze do zmieniających friend B<int>::B<int>()friend B<int>::B() wyników w

../src/main.cpp:15: error: no ‘void B::B()’ member function declared in class ‘B’

podczas usuwania szablonu całkowicie

class A; 
class B 
{ 
private: 
    A* a; 

public: 
    B(); 
}; 


class A : public B 
{ 
private: 
    friend B::B(); 
    int x; 
}; 


B::B() 
{ 
    a = new A; 
    a->x = 5; 
} 

int main() { return 0; } 

kompiluje i wykonuje dobrze - pomimo mojego IDE mówiącego przyjaciela B :: B() jest nieprawidłowa składnia?

+0

Visual C++ 2008 nie akceptuje 'przyjaciela B :: B()', ale ma to miejsce w przypadku Comeau. –

+0

Visual C++ 2008 akceptuje 'przyjaciela B :: B ()' chociaż, nawet jeśli rozszerzenia językowe są wyłączone. Dlaczego GCC (4.1) nie zaakceptuje tego? – Kyle

+1

Jeśli ktoś nie wie (ja nie), konstruktorzy mogą zostać ogłoszeni przyjaciółmi: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#263 –

Odpowiedz

5

Za uchwałą do CWG defect 147 (uchwała została włączona do C++ 03), poprawny sposób wymienić nontemplate konstruktora klasy specjalizacji szablonu jest:

B<int>::B(); 

i nie

B<int>::B<int>(); 

Jeśli te ostatnie były dozwolone, istnieje niejednoznaczność, gdy masz specjalizację szablonu konstruktora specjalizującego się w szablonach klas: czy drugi szablon <int> byłby dla szablonu klasy lub szablonu konstruktora? (Patrz raport awaryjności umieszczonego powyżej celu uzyskania dalszych informacji na temat tego)

więc poprawny sposób zadeklarować konstruktor specjalizacji szablonu klasy jako przyjaciela jest:

friend B<int>::B(); 

Comeau 4.3.10.1 i Intel C++ 11.1 oba przyjmują tę formę. Ani program Visual C++ 2008, ani Visual C++ 2010 nie akceptują tego formularza, ale oba akceptują (niepoprawny) formularz friend B<int>::B<int>(); (będę zgłaszał raport o usterkach w programie Microsoft Connect).

gcc nie akceptuje żadnej z wersji wcześniejszych niż wersja 4.5. Bug 5023 został zgłoszony przeciwko gcc 3.0.2, ale żądana rozdzielczość w raporcie o błędzie była nieprawidłową formą. Wygląda na to, że postanowienie dotyczące bug 9050 rozwiązuje również ten problem, a gcc 4.5 akceptuje poprawny formularz. Georg Fritzsche potwierdził to w komentarzu do pytania.

+1

Wysłałem raport o usterkach do Microsoft Connect: https://connect.microsoft.com/VisualStudio/feedback/details/558796/. Mogłem tylko odtworzyć problem, gdy specjalizacja szablonu klasy jest klasą podstawową klasy próbującej zaprzyjaźnić się z konstruktorem. Po usunięciu dziedziczenia z kodu, Visual C++ wydaje się poprawnie obsługiwać deklarację znajomego. Jeśli możesz znaleźć prostszy przypadek testowy, możesz go opublikować jako komentarz do tego raportu o usterce. –

+0

Co ciekawe, opuścili dziwną regułę w klauzuli 5.1.1/6 (FCD), która mówi "gdzie nazwa-klasy :: nazwa-klasy jest używana, a dwie nazwy-klasy odnosząsię do tej samej klasy, ta notacja nazywa konstruktora (12.1). ". Ta zasada jest nie tylko bezużyteczna w obecności 3.4.3.1/2 i jest z nią sprzeczna ('B :: B ' nazwałaby dowolne konstruktory inne niż szablonowe). Miałem wrażenie, że numer # 147 miał usunąć tę dziwną regułę.Uważam, że klauzula 5 (która dotyczy wyrażeń) jest i tak niewłaściwym miejscem dla takich zasad (a prawa to 3.4). –

+0

@Johannes: Myślę, że § 12.1/1 (C++ 03 i FCD) ma ten sam problem: "... nazwa klasy konstruktora, po której następuje lista parametrów służy do deklarowania lub definiowania konstruktora." –

0

Podejrzewam, że jesteście tu, na terytorium dziwactw, tutaj z szablonowymi konstruktorami. Ta kompilacja i działa poprawnie na VS2010, ale generuje przepełnienie stosu, gdy domyślny konstruktor A wywołuje domyślny konstruktor B, który następnie ponownie tworzy instancję A.

+0

Tak, zdecydowanie nie chcę próbować tworzyć tych klas, które napisałem, hehe. Zwróć uwagę na jawne 'int main() {return 0; } 'Włączyłem tam, tzn." Nie próbuj robić nic innego ":-) – Kyle

1

A powód, dla którego IDE pokazuje przyjaciela B :: B() jako nieważną składnię w tym drugim przypadku? Błąd IDE.

Obejście, które znalazłem w gcc dla szablonu, jeśli nie można uaktualnić, to przenieść implementację B() do funkcji składowej, unieważnić B :: init() i zamiast tego udzielić przyjaźni. Założę się, że to również zamknie twoje IDE.

Nawet jeśli się to skompilować, choć będziesz napotkasz problem przepełnienia stosu, jak tylko spróbować instancji B.

+0

Prawda, nie wygląda na to, że 'A' powinno odziedziczyć po' B', a to rozwiązałoby problem nieskończonej rekurencji. –

1

Czy nie pomaga typedef? na przykład

class A : public B<int> 
{ 
    typedef B<int> Base; 
    friend Base::Base(); 
    int x; 
}; 

Edycja: Projekt Komitet Końcowy C++ 0x obejmuje następujące języka w sekcji 3.4.3.1 [class.qual]

In a lookup in which the constructor is an acceptable lookup result and the nested-name-specifier nominates a class C: if the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (Clause 9), or if the name specified after the nested-name-specifier is the same as the identifier or the simple-template-id’s template-name in the last component of the nested-name-specifier, the name is instead considered to name the constructor of class C.

brzmi jak nazwa (Base) określony po określeniu nazwy zagnieżdżonej (Base::) jest takie samo jak identyfikator w ostatnim komponencie opisywanej nazwy użytkownika, więc ten kod nazwij konstruktora.

Ale nie mogę pogodzić się z tym rozdziale 12.1 [class.ctor]:

Because constructors do not have names, they are never found during name lookup

Naprawdę? Jak działa ten język w wersji 3.4.3.1?

A typedef-name shall not be used as the class-name in the declarator-id for a constructor declaration.

To wydaje się dość oczywiste, chyba że sekcja 12.1 wydaje się dyskutować jedynie deklarację wprowadzającą z konstruktora jako ustęp 1 wyklucza nested-NAME specyfikator, friend i using. Jeśli to ma zastosowania do deklaracji przyjaciel, wydaje się również, aby zabronić friend Base::B();

A special declarator syntax using an optional sequence of function-specifiers (7.1.2) followed by the constructor’s class name followed by a parameter list is used to declare or define the constructor.

Those Funkcjonalnie specyfikatorówinline, virtual, explicit i konstruktorzy nie mogą być virtual tak.

+0

Niestety, nie, ponieważ konstruktora nie można nazwać w ten sposób. Musiał to być "przyjaciel Base :: B()", który jest poprawny, ale którego Visual C++ nie akceptuje, lub 'przyjaciel Base :: B ()', który jest niepoprawny, ale który akceptuje program Visual C++. –

+0

@ James: Czy twoim zdaniem akapit 3 12.1 odnosi się tylko do deklaracji wprowadzającej (w klasie), takiej jak ust. 1, lub obejmuje również deklaracje "przyjaciela" i "używania". –

+0

Nie sądzę, że cytat z §3.4.3.1/2 pochodzi z FCD - w FCD druga część rozpoczynająca się od "lub jeśli nazwa określona ..." jest poprzedzona "w deklaracji użycia, która jest deklaracją członkowską ", więc nie ma tu zastosowania. Moją obecną myślą jest to, że §12.1/3 ma zastosowanie w dowolnym momencie deklaracji konstruktora, w tym w deklaracji przyjaciela. Zgadzam się, że sposób w jaki jest sformułowany, zabrania 'przyjaciela Base :: B();'. Nie jestem pewien, czy to była intencja. Próbuję znaleźć propozycję lub wadę, która spowodowała dodanie tego zdania, ale nie miałem szczęścia. –

Powiązane problemy