2014-10-25 7 views
5

Sto cercando di capire perché non possiamo usare il tipo annidato attraverso l'espressione di accesso membro della classe. Per esempio, abbiamo la seguente classe:Perché non possiamo usare il tipo annidato attraverso l'espressione di accesso membro della classe?

struct U 
{ 
    struct A 
    { 
     static int v; 
     int a; 
    }; 

    struct B 
    { 
     int b; 
    }; 
}; 

U a; 

typedef a.A T; //'a' does not name a type 

int main() 
{ 
    std::cout << typeid(a.A).hash_code(); //invalid use of 'struct U::A' 
    struct a.A b;       //trying to declare a variable of type U::A 
              //error: expected unqualified-id before '.' token 
    a.A b;        //the same as above 
              //error: expected unqualified-id before '.' token 
    a.A.v = 5;       //error: expected unqualified-id before '.' token 
} 

DEMO

Lo Standard dice:

Sezione N3797::5.2.5/2 [expr.ref]

For the first option (dot) the first expression shall have complete class type. For the second option (arrow) the first expression shall have pointer to complete class type. The expression E1->E2 is converted to the equivalent form (*(E1)).E2; the remainder of 5.2.5 will address only the first option (dot).In either case, the id-expression shall name a member of the class or of one of its base classes.

considerando che la sezione N3797::9.2/1 [class.mem] dà una definizione di classe membro:

Members of a class are data members, member functions (9.3), nested types, and enumerators.

Quindi non riesco a vedere la restrizione per tale utilizzo di tipo annidato. Perchè no?

risposta

4

Più in basso in [expr.ref]

(4.4) If E2 is a nested type, the expression E1.E2 is ill-formed.

+0

Questa è una clausola sotto "* Se E2 è dichiarato di avere tipo" riferimento a T, "*", non penso che questo si applichi qui. – luk32

+0

@ luk32 "Se E2 è dichiarato avere il tipo" riferimento a T ", allora E1.E2 è un lvalue, il tipo di E1.E2 è T. ** In caso contrario, si applica una delle seguenti regole. **" – user657267

+0

Oh L'ho capovolto L'ho letto 3 volte pensando che doveva essere un riferimento a T. Però avrei messo più contesto in risposta. Penso che sia importante avere una regola intera. IMO è questo. – luk32

2

Lo standard sta parlando del tipo di oggetto rappresentato da una variabile.
È necessario accedere allo scope della classe, non alla variabile.

E per accedere ai membri della classe annidata da U, è necessario creare un membro di quel tipo, ci sono 2 modi per farlo:

struct U 
{ 
    struct A 
    { 
     static int v; 
     int a; 
    } VarNameForA; 

    struct B 
    { 
     int b; 
    }; 
    B VarNameOfB; 
}; 

utilizzare l'operatore ambito :: per accedere ai tipi .

Ecco come si fa tutto si è tentato:

U a; 

typedef decltype(a) varType; 
typedef varType::A nestedType; 

int main() 
{ 
    std::cout << typeid(U::A).hash_code(); 
    struct U::A b;       
    U u; 
    u.VarNameForA.a = 5; 
    u.VarNameOfB.b = 6; 

    U::A::c = 3;       

} 

Per i membri che si utilizzano operatore dot (.), per i tipi e statica è necessario utilizzare l'operatore scope ::.

+0

Penso che OP sia a conoscenza di:, ma indica che N3797 :: 5.2.5/2 dice il contrario. –

+0

Onestamente, non penso che questo risponda alla domanda. La domanda non è come far funzionare il codice e definire correttamente tutti i tipi. Ma perché non puoi accedere al tipo tramite '.', mentre le citazioni fornite suggeriscono che dovresti essere in grado di farlo. Il tipo annidato è elencato come membro, quindi anche la tua risposta suggerire che '.' dovrebbe andare bene. – luk32

2

Si tenta di utilizzare a.A come identificatore di tipo.

Le regole di accesso membro della classe (5.2.5) si riferiscono ad valgono solo per espressioni non per progettisti di tipo e nomi dei tipi (5/1: "Un'espressione è una sequenza di operatori e operandi che specifica un calcolo Un'espressione può dare un valore e può causare effetti collaterali. ").

È necessario osservare le regole per gli identificatori di tipo nella sezione 7.1.6 dello standard. Non è previsto l'operatore . previsto lì. I tipi elaborati (7.1.6.3) utilizzano uno :: per combinare componenti su un nome di tipo.

Il messaggio di errore clang è più esplicito di quello che si ottiene per il vostro compilatore: "non può usare operatore punto su un tipo").

A proposito, il tuo U è una struttura vuota (senza dati) con la sola definizione di tipi annidati. Questo è il motivo per cui anche a.A.v = 5; è un errore: qui non è presente alcun lvalue che potrebbe essere assegnato.

+0

Quindi 'decltype (a.A)' funziona? Potrei anche indicare dove si dice che 5.2.5 si applica solo alle espressioni? – luk32

+0

Ottima domanda! Std 7.1.6.2 definisce decltype (espressione) come identificatore di tipo. Ma le espressioni sono sui valori, non sui tipi e aA non denota un valore (* 7.1.6.2.4: Se non esiste tale entità, (...) il programma è mal formato *) – Christophe

+0

Il messaggio di errore da clang per il decltype (aA) è diverso da quelli precedenti. dice "non si può riferire al membro del tipo 'A' in 'U' con '.'" che suggerisce che il compilatore applica in questo caso la regola menzionata dall'utente 657267. – Christophe

Problemi correlati