Domanda

Viene compilato il seguente programma:

template <const int * P>
class Test{};

extern const int var = 42; //extern needed to force external linkage

int main()
{
    Test<&var> test;
}

Questo, tuttavia, no, il che è una sorpresa per me:

template <const int * P>
class Test{};

extern const int var = 42; //extern needed to force external linkage
extern const int * const ptr = &var; //extern needed to force external linkage
int main()
{
    Test<ptr> test; //FAIL! Expected constant expression.
}

Esempio alternativo:

int main()
{
   const int size = 42;
   int ok[*&size]; //OK

   const int * const pSize = &size;
   int fail[*pSize]; //FAIL
}

Ho concluso che un puntatore non può essere un'espressione costante indipendentemente dal fatto che sia const e inizializzato con un'espressione costante.

Domande:

  1. La mia conclusione è vera?
  2. In tal caso, perché un puntatore non può essere un'espressione costante?In caso contrario, perché i programmi di cui sopra non vengono compilati?
  3. C ++ 0x (C ++ 11, se vuoi) cambia qualcosa?

Grazie per eventuali approfondimenti!

È stato utile?

Soluzione

È un po 'più complicato. In C ++ 03 e C ++ 11, &var è un'espressione costante se var è una variabile di ambito statica / di classe locale o dello spazio dei nomi. Questa è chiamata espressione costante di indirizzo. L'inizializzazione di una variabile del puntatore di ambito statico o dello spazio dei nomi di una classe con tale espressione costante è garantita prima dell'esecuzione di qualsiasi codice (fase di inizializzazione statica), poiché si tratta di un'espressione costante.

Tuttavia solo a partire da C ++ 11, una variabile puntatore constexpr che memorizza l'indirizzo &var può anche essere usata come espressione costante di indirizzo e solo da C ++ 11, puoi dereferenziare una costante di indirizzo espressione (in realtà, puoi dereferenziare ancora di più - anche gli indirizzi degli elementi dell'array locale, ma manteniamolo ontopico) e se si riferisce a una variabile integrale costante inizializzata prima della dereferenziazione oa una variabile constexpr, ottieni di nuovo un'espressione costante (a seconda il tipo e la categoria di valore, il tipo di espressione costante può variare). In quanto tale, quanto segue è valido C ++ 11:

int const x = 42;
constexpr int const *px = &x;

// both the value of "px" and the value of "*px" are prvalue constant expressions
int array[*px];
int main() { return sizeof(array); }

In tal caso, perché un puntatore non può essere un'espressione costante? In caso contrario, perché i programmi di cui sopra non vengono compilati?

Questa è una limitazione nota nella formulazione dello Standard: attualmente consente solo altri parametri del modello come argomenti o & object, per un parametro del modello di tipo puntatore. Anche se il compilatore dovrebbe essere in grado di fare molto di più.

Altri suggerimenti

Non è ancora consentito in C ++ 0x. temp.arg.nontype richiede:

Un argomento-modello per un parametro modello non di tipo e non di modello deve essere uno di:

  • per un parametro-modello non di tipo di tipo integrale o di enumerazione, un'espressione costante convertita (5.19) del tipo di parametro-modello; o
  • il nome di un parametro di modello non di tipo; o
  • un'espressione costante (5.19) che designa l'indirizzo di un oggetto con durata di archiviazione statica e collegamento esterno o interno o una funzione con collegamento esterno o interno, inclusi i modelli di funzione e la funzione template-ids ma escludendo i membri della classe non statica, espressa (ignorando le parentesi) come & id-expression , tranne per il fatto che & può essere omesso se il nome si riferisce a una funzione o matrice e deve essere omesso se il parametro-template corrispondente è un riferimento; o
  • un'espressione costante che restituisce un valore di puntatore nullo (4.10); o
  • un'espressione costante che restituisce un valore del puntatore a un membro nullo (4.11); o
  • un puntatore a un membro espresso come descritto in 5.3.1.

risposta originale:

  1. In C ++ 03, solo le espressioni integrali possono essere espressioni costanti.
  2. Perché lo standard lo dice (naturalmente).
  3. In C ++ 0x, n3290 include esempi che utilizzano constexpr su un puntatore. Quindi quello che stai provando ora dovrebbe essere possibile, anche se ora devi usare la parola chiave constexpr invece di const di primo livello.

È coinvolto anche un bug di gcc, g ++ rifiuta gli esempi della bozza standard di utilizzo di constexpr valido .

Il problema è perché il tuo programma C ++ può essere caricato in qualsiasi punto della memoria, quindi l'indirizzo di un var globale potrebbe essere diverso ogni volta che esegui il programma.Cosa succede se esegui il programma due volte?var si trova ovviamente in due posizioni differenti.

Ancora peggio, nel tuo esempio, prendi l'indirizzo di una variabile nello stack!guarda questo:

void myfunction( unsigned int depth) {
     const int myvar = depth;
     const int * const myptr = &myvar;
     if (depth)
         myfunction(depth-1);
}

Se main chiama myfunction (3), vengono creati 3 myvar in posizioni separate.Non c'è modo per il tempo di compilazione di sapere come vengono creati molti myvar, tanto meno le posizioni esatte.

Infine: dichiarare una variabile come const significa: "Lo prometto" e non significa che è una costante del tempo di compilazione.Vedi questo esempio:

int main(int argc, char** argv) {
    const int cargc = argc;
    char* myargs[cargc]; //the size is constant, but not a _compile time_ constant.
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top