Definizione dei membri interi cost const nella definizione della class

La mia comprensione è che C ++ consente di definire i membri const statici all’interno di una class purché si tratti di un tipo intero.

Perché, allora, il codice seguente mi dà un errore di linker?

#include  #include  class test { public: static const int N = 10; }; int main() { std::cout << test::N << "\n"; std::min(9, test::N); } 

L’errore che ottengo è:

 test.cpp:(.text+0x130): undefined reference to `test::N' collect2: ld returned 1 exit status 

È interessante notare che se commento la chiamata a std :: min, il codice compila e collega bene (anche se test :: N è anche referenziato sulla riga precedente).

Qualche idea su cosa sta succedendo?

Il mio compilatore è gcc 4.4 su Linux.

La mia comprensione è che C ++ consente di definire i membri const statici all’interno di una class purché si tratti di un tipo intero.

Sei un po ‘corretto. È consentito inizializzare gli integrali const statici nella dichiarazione della class, ma questa non è una definizione.

http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/cplr038.htm

È interessante notare che se commento la chiamata a std :: min, il codice compila e collega bene (anche se test :: N è anche referenziato sulla riga precedente).

Qualche idea su cosa sta succedendo?

std :: min prende i suoi parametri per riferimento const. Se li prendi in base al valore non avresti questo problema, ma dal momento che hai bisogno di un riferimento hai bisogno anche di una definizione.

Ecco il capitolo / verso:

9.4.2 / 4 – Se un membro di dati static è di tipo const integrale o const enumeration, la sua dichiarazione nella definizione di class può specificare un inizializzatore costante che deve essere un’espressione di costante integrale (5.19). In tal caso, il membro può apparire in espressioni costanti integrali. Il membro deve ancora essere definito in un ambito namespace se viene utilizzato nel programma e la definizione dell’ambito dello spazio dei nomi non deve contenere un inizializzatore .

Vedi la risposta di Chu per una ansible soluzione alternativa.

L’esempio di Bjarne Stroustrup nelle sue domande frequenti sul C ++ suggerisce che sei corretto e che hai bisogno di una definizione solo se prendi l’indirizzo.

 class AE { // ... public: static const int c6 = 7; static const int c7 = 31; }; const int AE::c7; // definition int f() { const int* p1 = &AE::c6; // error: c6 not an lvalue const int* p2 = &AE::c7; // ok // ... } 

Dice “Puoi prendere l’indirizzo di un membro statico se (e solo se) ha una definizione fuori dalla class” . Il che suggerisce che funzionerebbe diversamente. Forse la tua funzione minima richiama gli indirizzi in qualche modo dietro le quinte.

Un altro modo per farlo, per tutti i tipi interi, è definire le costanti come enumerazioni nella class:

 class test { public: enum { N = 10 }; }; 

Non solo int. Ma non è ansible definire il valore nella dichiarazione della class. Se hai:

 class classname { public: static int const N; } 

nel file .h devi avere:

 int const classname::N = 10; 

nel file .cpp.

Ecco un altro modo per aggirare il problema:

 std::min(9, int(test::N)); 

(Penso che la risposta di Crazy Eddie descriva correttamente perché il problema esiste.)

A partire da C ++ 11 puoi (e vuoi) usare:

static constexpr int N = 10;

questo non richiede di definire la costante in un file .cpp.

Il linguaggio C ++ consente ai membri const const di essere definiti all’interno di una class

No, 3.1 §2 dice:

Una dichiarazione è una definizione a meno che non dichiari una funzione senza specificare il corpo della funzione (8.4), contenga lo specificatore esterno (7.1.1) o una specifica di collegamento (7.5) e né un inizializzatore né un corpo di funzione, dichiara dati statici membro in una definizione di class (9.4), è una dichiarazione del nome di class (9.1), è una opaque-enum-declaration (7.2), oppure è una dichiarazione typedef (7.1.3), una dichiarazione d’uso (7.3. 3), o una direttiva using (7.3.4).