alexas napisał(a)
OK, a gdyby F było klasą to na początku trzeba byłoby napisać już jej definicję + w środku jej deklarację czy definicję klasy (...) Czyli jeśli przed właściwą klasą (nazwą na końcu tej "sklejanki" operatorami ::) występują jakieś inne klasy to muszą one być już wcześniej wszystkie zdefiniowane, tak ?
Tak.
W innym wypadku, nie jestes w stanie wyrazic poprawnie deklaracji klasy X.
Niech bedzie np. ze X ma byc w klasie Cos, ktore jest w klasie Swiat w namespace Bum, czyli Bum::X, porownaj:
class X;
oznacza fwd-decl z 'okolicy lokalnej' - cos innego
Bum::class X;
oznacza fwd-decl Bum::X - cos innego, w dodatku wymaga aby Bum::X bylo juz-znane
namespace Bum { class X; }
oznacza fwd-decl X w namespace Bum - nie wymaga, aby X bylo znane, ale znaczy cos innego niz chcemy
namespace Bum { class Swiat::Cos::X; }
niby ok, ale wymaga aby Swiat oraz Cos oraz X byly juz-znane..
namespace Bum { class Swiat{ .... class Cos{ .... class X; .... } .... } }
oznacza to co chcemy - fwddecl nieznanej-jeszcze nazwy X, bedacej class, siedzacej w class Cos, w class Swiat, w namespace Bum. ALE: COS oraz SWIAT sa definicjami! Dlatego umiesciem tam wielokropki -- pola, metody, wszystko tam musi byc podane. Tylko i wylacznie X jest forward-deklaracja, zreszta niskiego sensu - poniewaz skoro "COS" jed deFINIOwane, to deFINIcja typu X tez sie musi w nim pojawic lada moment ponizej... Stad wynika wprost "wymaganie", aby Swiat i Cos byly "zdefiniowane".. A tak mówiąc scisle - to nie wymaganie. Ty po prostu nie jestes w stanie zadeklarowac X inaczej niz poprzed podanie definicji Cos,Swiat siedzacych w Bum..
Z teog wlasnie "malego" powodu, wprowadzono NAMESPACE'y. I tym przede wszystkim sie one roznia od klas+static. Zwroc uwage, ze w/w przypadek wymusza podanie pelnej definicji Swiat i Cos, a co z namespace Bum? ona NIE MUSI byc pelna! W przeciwienstwie do ciał typów/metod/etc, ciała namespace'ow sa zawsze OTWARTE i przez to "zawsze nie do końca pełne" - mam nadzieje ze nie musze tlumaczyc. Wyciagajac typ X na zewnatrz i umieszczajac go w analogicznym pod-namespace:
namespace Bum { namespace Swiat2 { namespace Cos2 { class X; } } }
jest absolutnie poprawna forward-decl., i od tej pory mozesz uzywac kwalifikowanego Bum::X a klasy Bum::Swiat oraz Bum::Cos moga sobie byc zadeklarowane/zdefiniowane (prawie) gdziekolwiek, i rowniez do tych namespace'ow mozesz w (prawie) dowolnym miejscu cos dodatkowego dopisac..
@Twoj przyklad, por.:
// forward decl:
namespace F_internal
{
class X;
}
...
...
namespace E
{
class A
{
friend class F_internal::X; // OK! zwroc uwage, ze typ F jest kompletnie nieznany
};
}
...
...
// i gdziestam kiedys definicja:
namespace F_internal
{
class X{};
}
// uzycie przez F:
using namespace F_internal; // po co sie meczyc.. import calego swojego _impl
class F
{
F() { delete new X(); } // ot tak, tak jakby X bylo w F - dzieki using - ale faktycznie jest w F_impl
};
alexas napisał(a)
Kolejna regułka, której się nie przyglądnąłem nigdy - nazwa w namespace zawsze ma przed sobą nazwę tej przestrzeni w której jest, tak ? Czyli jak w deklaracji przyjaźni występuje np. nazwa B::F, a ta deklaracja przyjaźni jest w klasie C, która jest w przestrzeni o nazwie H, to deklaracja przyjaźni tak na prawdę wygląda tak: H::F ?
Nie. Moze tak wygladac, ale nie musi.
W poprzednim poście starałem się Ci pokazać różnice pomiędzy kwalifikowanymy identyfikatorami, a niekwalifikowanymi
X - unqualified
Swiat::X - qualified
koniec rozroznienia. Innych typow identyfikatorow nie ma. wszystkie nazwy sa albo takie, albo takie.
Roznica miedzy nimi brzmi:
- niekwalifikowane podlegaja lokalnemu dopasowywaniu do najblizszej pasujacej definicji osiagalnej w danym scopie
- kwalifikowane sa rozumiane jako twarde wiazania do podanych konkretnych definicji lezacych na konkretnej podanej sciezce, patrzac od samej gory drzewa nazw
Co wiecej, zwroc uwage na rekurencyjnosc tych terminow, w ponizszej nazwie:
namespace Ah{namespace Oh{namespace Uh{
...
blahblah Swiat::Cos::X ;
...
}}}
X, Cos - sa kwalifikowane
Swiat - jest niekwalifikowany
-> to znaczy, ze lokalizacja identyfikatorow 'Cos' i 'X' jest sztywna i konkretna, i kompilator bedzie sie spodziewal, ze sa znane, i bedzie ich szukal odpowiednio we "Swiat::" oraz "Cos::"
-> to znaczy ze lokalizacja identyfikatora 'Swiat' jest luzna i zostanie dopasowana kontekstowo
--->> to znaczy, ze Swiat::X != Ah::X !!!!
ponizsze przyklady pokazuja w jaki sposob dziala nazwy (nie)kwalifikowane i maja nie wiele wspolnego z "friend". on jest tutaj tylko jako przyklad uzycia jakiejs nazwy. wszystko rozbija sie o scope'y i kwalifikowanie nazw!
namespace E // przyklad 1
{
class A
{
friend struct X;
int z;
struct X{ X(){A().z = 1;}};
};
}
poprawny. friend struct X oznacza kontekstowe X, ale w momencie jego definicji nie ma nic takiego (jest ponizej) - wiec nie dopasowuje sie do inner-structX. kod jest poprawny gdyz na ma dostep ot tak, z racji bycia inner-..
namespace E // przyklad 2a
{
class A
{
friend struct X;
int z;
};
struct X{ X(){A().z = 1;}};
}
poprawny, na mocy tego co wczesniej, struct X lezy w kontekscie (namespace F) wyrazenia friend-X
namespace E // przyklad 2b
{
struct X;
class A
{
friend struct X;
int z;
};
struct X{ X(){A().z = 1;}};
}
poprawny, na mocy tego co wczesniej, struct X lezy w kontekscie (namespace F) wyrazenia friend-X, fwd-decl nic nie wnosi
namespace E // przyklad 3
{
class A
{
friend struct X;
int z;
struct X{ X(){A().z = 1;}}; // qualified: ::E::A::X
};
struct X{ X(){A().z = 1;}}; // qualified: ::E::X
}
rowniez poprawny. friend faktycznie odnosi sie do E::X a nie E::X
z kolei
namespace E // przyklad 4a
{
class A
{
int z;
struct X{ X(){A().z = 1;}}; // qualified: ::E::A::X
friend struct X;
};
struct X{ X(){A().z = 1;}}; // qualified: ::E::X
}
jest bledny! bardziej-lokalny X zdefiniowany PRZED friend-X przykrywa zewnetrzne X
namespace E // przyklad 4b
{
class A
{
int z;
struct X;
friend struct X;
struct X{ X(){A().z = 1;}}; // qualified: ::E::A::X
};
struct X{ X(){A().z = 1;}}; // qualified: ::E::X
}
jest bledny!! bardziej-lokalny X zadeklarowany PRZED friend-X przykrywa zewnetrzne X, fwd-decl spowodowal zmiane kontekstu! porownaj z przykladem 2b!
//struct X; // przyklad 5a
namespace E
{
class A
{
friend struct X;
int z;
};
}
struct X{ X(){E::A().z = 1;}};
bledny, gdyz friend-X odnosi sie do czegos kontekstowego, ale struct-X lezy poza jego kontekstem (namespace F). fwd-decl X nic nie pomoze
namespace E // przyklad 5b
{
//struct X;
namespace F
{
class A
{
friend struct X;
int z;
};
}
struct X{ X(){E::F::A().z = 1;}};
}
bledny, gdyz friend-X odnosi sie do czegos kontekstowego, ale struct-X lezy poza jego kontekstem (namespace F). fwd-decl X nic nie pomoze
namespace E // przyklad 6a
{
//struct X;
namespace F
{
class A
{
friend struct ::X;
int z;
};
}
struct X{ X(){E::F::A().z = 1;}};
}
bledny, gdyz friend-X odnosi sie do czegos w default/global namespace. fwd-decl nie pomoze.
//struct X; // przyklad 6b
namespace E
{
namespace F
{
class A
{
friend struct ::X;
int z;
};
}
}
struct X{ X(){E::F::A().z = 1;}};
bledny, ale fwd-decl zmieni kontekst i poprawi! ::X mowi o konkrecie w global namespace, wiec jesli odkomentujesz fwd-decl - szukany identyfikator bedzie juz istnial w miejscu friend-::X
namespace E // przyklad 7a
{
class A
{
private: struct F
{
struct X{};
};
friend class F::X;
};
namespace F
{
struct X{ X(){ E::A::F::X(); }}; // test dostepu
}
}
bledny. friend-F::X mowi o konkretnym 'X' w kontekstowym 'F', wiec dopasowuje sie do zdefiniowanego tuz-wczesniej inner-structX, ktory i tak by mial dostep. strukturaX zewnetrza nie dostaje dostepu
namespace E // przyklad 7b
{
class A
{
friend class F::X;
private: struct F
{
struct X{};
};
};
namespace F
{
struct X{ X(){ E::A::F::X(); }};
}
}
bledny. friend-F::X mowi o konkretnym 'X' w kontekstowym 'F', jednak w punkcie tym F jest kompletnie nie znane! wiec jaka mowa o konkretnym X w F?
namespace E
{
namespace F
{
struct X;
}
class A
{
friend class F::X; // zagadka:) poprawne? ktore F::X ?
private: struct F
{
struct X{};
};
};
namespace F
{
struct X{ X(){ E::A::F::X(); }};
}
}
poprawny! friend-F::X mowi o konkretnym X w jakims F; patrzac z kontekstu tego miejsca, F zostalo wczesniej zadeklarowane jako namespace, w ktorym fwd-decl zadeklarowalo jakas nieznana strukture X; F::X na mocy tego zostaje rozpoznane jako (niecalkiem znana jeszcze) struktura X-w-F-w-E, a nie do inner-class ktora jest zadeklarowana po friend'zie. dzieki temu pozniejsza faktyczna implemetnacja namespaceF::structX ma dostep do prowatnej innerstruct..
przyklady sprawdzane na "gcc version 4.4.3 20100205 (release) (PLD-Linux)", niestety w chwili pisania innego pod reka nie mam. chetnie sie dowiem czy np. Comeau sie z w/w zgadza?