[C++] Kilka pytań odnośnie deklaracji przyjaźni

Mam kilka spraw odnosnie deklaracji przyjaźni w klasach. Uczę się z książki “Symfonia C++ standard”.

Pierwsza rzecz to taki kod podany przez autora:

class druga; //<--- deklaracja zapowiadająca

class pierwsza{

    friend class druga;

};

class druga{

    friend class pierwsza;

};

Pierwsza linijka - sprawdzilem - jest zbędna prawda ? Bo dekl. zapowiadająca jest jednocześnie w deklaracji przyjaźni jakby w liniii 3. Druga sprawa to deklaracja przyjaźni z jakąś funkcją składową. Jeżeli np. klasa X definiuje przyjaźń z funkcją PRZ::fun to klasa PRZ musi być już wcześniej zdefiniowana. Grębosz wspomniał, że tak jest zeby np. sprawdzic czy taka funkcja rzeczywiście tam jest. Są jeszcze inne argumenty ? Trzecia sprawa łączy się z powyższą. Jeżeli mamy taką sytuację jak wyżej to funkcja PRZ::fun musi być zdefiniowana na zewnątrz klasy PRZ, po definicji klasy X, prawda ? Bo skoro w swoim ciele korzysta ze skladnikow tej klasy X, to X musi być już zdefiniowana, tak ? Czwarta sprawa dotyczy specyfikatora ‘static’. W Symfonii jest napisane:

Czyli aby funkcja posiadała łączenie wewnętrzne i była przyjacielem klasy to powinna znaleźć się nad definicją danej klasy:

static void fun(int, int)

{ }

class A

{

    friend void fun(int, int);

};

Zgadza się ?

Piąta i ostatnia sprawa dot. tego fragmentu z ksiązki:

Pomijając fakt, ze autor się pomylił tutaj, bo nigdy nie ma takich przydomków przed funkcją jak mutable, register i auto - to dlaczego static i extern są zabronione ?

Dzięki za pomoc :wink:

Add 1 TAK ale ta deklaracja ma wtedy tylko zakres tej klasy ! Więc jeżeli chcesz jeszcze gdzieś puźniej użyć nazwy “druga”, ale przed definicją to musisz pozostawić 1 linijkę.

Czyli GNU G++ jest niezgodny ze standardem bo dopuszcza cos takiego :?:

class A{friend void fun();

g++ w wersji 4.4.1 działa zgodnie z oczekiwaniami, tj. wywala się w ósmej linii.

ISO/IEC 14882:2003 (11.4.6) stawia sprawę jasno:

Można to uzasadnić tym, że “storage class” (nie czytałem żadnej polskiej/tłumaczonej książki i naprawdę nie wiem, jak się to nazywa) jest w tym przypadku ustalane automatycznie. Jeśli pojawiła się deklaracja funkcji/klasy przed deklaracją przyjaźni do niej, “storage class” jest już określone, wpp wybierany jest “external linkage” - ISO/IEC 14882:2003 (11.4.3).

“deklaracja funkcji/klasy” :?: Deklaracja klasy nie moze też zawierać żadnych specyfikatorów (bo klasa nie ma linkage jakby), więc raczej poprawnie będzie:

Racja, zagalopowałem się - powinno być właśnie tak.

Ale to akurat nie jest prawdą - w C++ niemal wszystko ma jakiś linkage (internal, external, no-linkage), choć specyfikatory odnoszą się jedynie do obiektów lub funkcji. W tym przypadku (jeśli zamiast fun byłaby jakaś klasa) byłby external linkage, z racji namespace scope - ISO/IEC 14882:2003(E) (3.5.4).

Czy mógłbyś podać ten fragment standardu, bo aktualnie nie mam go pod ręką :?: byłbym Ci bardzo wdzięczny

Napisałem, że klasa nie ma linkage, bo myślałem, że jak może mieć, skoro jak chcemy jej używać w danym pliku (jednostce translacyjnej) to za każdym razem musimy włączać jej definicję… Nie piszemy przecież

extern class A;

Jasne:

Tutaj znajduje się ISO/IEC 14882:2003, choć wydaje mi się, że nie powinno go tu być, ale to akurat nie moje zmartwienie.

Dzięki.

A no właśnie - nic nie jest napisane dokładniej. Więc powtórzę moje pytanie - dlaczego nie jest napisane ze klasa ma no-linkage, skoro nie możemy napisać:

extern class A;

(a storage class may not be specified here).

Z gory ponownie dzieki za pomoc :wink:

Hmm, trochę się tego nazbierało :smiley:

Z tym linkage w klasach, to nie jest do końca na poważnie (a przynajmniej takie odniosłem wrażenie), a już nie tak jak w przypadku obiektów/funkcji. Pokaż mi kod, w którym chciałbyś użyć extern class … lub static class … (static class mógłby być nieco mylący) Moim zdaniem to bez sensu, bo do typów odnoszą się inne wymagania (complete/incomplete). Faktyczne rozróżnienie występuje np. w przypadku no-linkage/external (a nie widziałem klasy z internal linkage):

template

void foo(const T&) {}


struct A {

  int v;

};


int main()

{

  A a;

  foo(a);


  struct B { int v; };

  B b;

  foo(b); // ■■■?!

}

W C++03 zabronione jest użycie szablonu na typie no-linkage (w przyszłości będzie możliwe).

Wydaje mi się, że to związek z tym, że to qualified-id - a nie simple identifier (jak w pierwszym pytaniu) - zatem:

// class C;


class A {

  int v;


  friend class B; // ok

  friend class ::C; // error

};


struct B { int v; };

struct C { int v; };

Ale konia z rzędem temu, kto mi powie, dlaczego w takim razie coś takiego jest w porządku (chyba, że to g++ nabroił):

class A {

  int v;


  friend void b(); // ok

  friend void ::c(); // ok ?!

};


void b() {}

void c() {}

Tak, a przynajmniej jej deklaracja. Piąte , też bym chciał móc napisać (choć prawdopodobnie nigdy bym z tego nie skorzystał :wink: ):

class A {

  int v;


  friend static void change_Av(const A &a, int _v) { a.v = _v; }

};

Zamiast:

class A;

static void change_Av(A&, int);


class A {

  int v;


  friend void change_Av(A &a, int _v) { a.v = _v; }

};

W standardzie stoi zakaz, ale bez uzasadnienia. Próbowałem zrozumieć komitet standaryzacyjny w tej kwestii, ale to chyba nie na moją głowę.

A propos “piąte”, możesz tak napisać, problem w tym że niepoprawnie rozumiesz klauzule static przy funkcjach, otóż oznacza ona że ta funkcja może być użyta jedynie w bieżącym pliku, więc możesz tak napisać (mimo - “możliwe że nigdy nie skorzystasz”):

class A{   int v;   friend void ::change_Av(A a, int _v) { a.v = _v; }  }; [/code]

Przy okazji wyjaśnia to punkt 4-ty:
[code=php]friend class ::C;       // errorfriend void ::c();      // ok

Jakby możliwa była konstrukcja z ‘extern’, to i ‘static’ nie byłby mylący przecież.

Rozumiem, że incomplete to deklaracja, a complete to definicja. No cóż ale mogłoby być tak jak ze zmiennymi globalnymi - definiujemy klasę w 1 pliku, a w pozostałych piszemy extern…

  1. Eee, ale ja pisałem o deklaracji z funkcją składową, a nie z klasą czy funkcją globalną :slight_smile: 2) Skoro już jesteśmy przy temacie to ten qualified-id to jest po prostu nazwa poprzedzona pustym operatorem zakresu :?: Ja slyszalem ze tego operatora uzywa sie jak chcemy odniesc sie do zaslonietej nazwy globalnej - a w Twoim przykładzie, nic takiego nie bylo więc po co on :stuck_out_tongue: ? 3) Nie wiem czemu też wystąpi błąd z tym operatorem ::. W drugim przykladzie jest tak jak mowiles - g++ zrobił błąd. Na Comeau C++ Online - kompilatorze - podobno - całkowicie zgodnym ze standardem jest błąd.

A właśnie. Wtedy wyjdzie chory zapis taki jak tutaj:

static void fun();

class A

{ friend void fun(); };

static void fun()

{}

Punkt 7.1.1.7 standardu mówi:

No właśnie, linkage’y powinny sie zgadzać, ale tak nie jest już w pierwszym przykładzie poniżej tego punktu.

static char* f(); // f() has internal linkage

char* f() // f() still has internal linkage

{ /* ... */ }

Pierwsza deklaracja ma internal, a druga (ktora jest zarazem definicją) ma external (domyślnie jest takie wybierane kiedy nie ma storage-class specifier) linkage. Ale pomimo tego błędu nie ma - dlaczego ? Strasznie to zagmatwane…

Jak juz wspomnialem - Comeau C++ Online wyświetli błąd w drugim przykładzie, który niby jest //ok…

Wielkie dzięki za pomoc :smiley:

PS. Im bardziej wgłębiam się w takie szczegóły, tym bardziej zaczynam rozumieć, dlaczego wg niektórych C++ jest dosłownie głupi.

Chodziło mi o nazewnictwo, przez ‘static class’ rozumiem klasę bez niestatycznych metod/obiektów (w C# da się to chyba nawet wymusić właśnie słówkiem static).

nie od razu linkujesz, nie wyobrażam sobie pliku obiektowego z częściową znajomością użytych typów.

qualified-id to także PRZ::fun - przykład, jak to widzi debugger intela. Generalnie rzeczy z :: (niekoniecznie na początku) są qualified-id, dlatego pozwoliłem sobie na przykład z global namespace - było najkrócej, bo jest za darmo. Chodziło o pokazanie różnicy między qualified-id a identifier, która może zepsuć kompilację.

static char* f(); // f() has internal linkage

char* f() // f() still has internal linkage

{ /* ... */ }

na to jest taka rada (7.1.1.6):

Tylko nie mów tego Stroustrupowi. :wink:

alex , to był Twój 1336. post - czyli prawie 1337. :stuck_out_tongue:

Wychodzi na to, że wszystkie nazwy z qualified-id (nie czytałem jeszcze tego artykułu co dałes bo sie stronka cos nie wczytuje - potem sprobuje znowu, wiec moge cos mowic nie tak) musza być zadeklarowane przed deklaracją przyjaźni.

Rozumiem to jesli chodzi o funkcję składową - jak to Grebosz powiedział: kompilator musi wiedziec ze taka funkcja tam jest.

Ale jeśli chodzi o zwykłą klasę to nie czaję już tego… Twój kod powinien się kompilować - no przecież:

// bez tego! class X;