2016-03-10 21 views
8

So che ci sono molte domande simili, ma domande in qualche modo diverse. Si tratta di circa la seguente situazione:Assegna un membro statico della classe constexpr alla variabile di runtime

#include <iostream> 
#include <array> 

template<typename T> class MyClass 
{ 
public: 
    static constexpr std::array<T,4> ARRAY {{4, 3, 1, 5}}; 
}; 

int main() 
{ 
    constexpr std::array<int, 4> my_array(MyClass<int>::ARRAY); // works fine -> can use the ARRAY to initialize constexpr std::array 

    constexpr int VALUE = 5*MyClass<int>::ARRAY[0]; // works also fine 

    int value; 
    value = my_array[0]; // can assign from constexpr 
    value = MyClass<int>::ARRAY[0]; // undefined reference to `MyClass<int>::ARRAY 

    std::cout << VALUE << std::endl; 
    std::cout << value << std::endl; 

    return 0; 
} 

Per quanto ho capito constexpr è per le costanti in fase di compilazione. Quindi il compilatore può fare già qualche calcolo, ad esempio per calcolare lo VALUE. Inoltre posso ovviamente definire uno constexpr std::array<,>, da cui posso assegnare i valori alle variabili di runtime. Mi aspetto che il compilatore imposti già value = 4 nel programma eseguibile, per evitare un'operazione di caricamento. Tuttavia, non posso assegnare direttamente dal membro statico, ottenendo l'errore

undefined reference to `MyClass<int>::ARRAY' 
clang-3.7: error: linker command failed with exit code 1 

che non ha senso per me, perché può essere fatto con una fase intermedia di un'altra variabile constexpr.

Quindi la mia domanda è: perché un membro statico di constexpr di una classe non può essere assegnato a una variabile di runtime?

Nota: nella mia MWE la classe è una classe modello, che non influisce sull'errore. Tuttavia, inizialmente ero interessato a questo caso particolare, che mi aspetto essere più generale come per una classe non modello.

(compilatore è clang++ o g++ con -std=c++11 - danno lo stesso errore)

Edit: @Bryan Chen: Hai dimenticato le linee di uscita. Sono aggiunti ora.

+0

'clang ++' hanno questo problema: http://coliru.stacked-crooked.com/a/e9698f2bb249e509. Ma 'g ++' funziona: http://coliru.stacked-crooked.com/a/5ef23fe29b0aaa28. Bug clang? –

+0

VS2015 dice questo: 'l'inizializzazione in classe per il tipo 'const std :: array ' non è stata ancora implementata; il membro statico rimarrà non inizializzato in fase di esecuzione ma è supportato l'uso in espressioni costanti. Potresti imbatterti in qualcosa di simile. –

+0

@Bryan: ero poco preciso. Ho installato g ++ 4.8.5, che non supporta ancora C++ 14. Ma mi aspettavo che funzionasse già per C++ 11 !? – marlam

risposta

7

Il undefined reference è un errore di linker. La regola è che se una variabile è odr-used allora deve avere una definizione. Questo vale anche per le variabili constexpr.

Come la maggior parte delle regole ODR, la violazione è un comportamento indefinito senza necessità di diagnostica (che potrebbe spiegare perché non è stata rilevata alcuna diagnostica per alcuni dei tuoi usi del valore).

Per correggere l'errore, aggiungere una definizione al di fuori della classe:

template<typename T> constexpr std::array<T,4> MyClass<T>::ARRAY; 

Dal momento che si tratta di un modello che si può effettivamente mettere questo nell'intestazione, a differenza del caso usuale in cui la definizione va esattamente in un .cpp file.


La questione principale è se ARRAY[0] conta come ODR-uso.Secondo this detailed post, in C++ 11 e C++ 14, l'indicizzazione di un array conta come odr-use, ma questo è stato modificato da DR 1926 archiviato contro C++ 14 per non essere odr-use.

Tuttavia, che sta parlando di array incorporati. IDK se lo stesso ragionamento si applica a std::array, trovo il testo di [basic.def.odr]/3 difficile da capire. In base allo informal definition on cppreference, uso della matrice, , poiché il suo valore di ritorno associa un riferimento alla matrice.

+0

Thx per la bella risposta. Ho capito bene che l'uso in fase di compilazione per altri oggetti constexpr non è _non_ odr-use e quindi non ha bisogno di definizione? In altre parole: il compilatore può calcolare "VALUE" usando l'operatore "[]" perché non ha bisogno di un riferimento a è (e quindi nessuna definizione), ma a "valore" di runtime non può essere calcolato perché è necessario un indirizzo e così una definizione è? – marlam

+0

@marlam Penso che siano entrambi odr-use, ma sono solo alcuni dettagli del compilatore che non conosco, che gli danno l'errore in un caso ma non nell'altro. –

4

Per questo motivo restituisco sempre oggetti constexpr da una funzione constexpr.

Codice modificato di seguito. Si noti che a causa di un deficit di C++ 14 in std::array<> è necessario restituire un const std::array per consentire a operator[] di funzionare.

#include <iostream> 

#include <iostream> 
#include <array> 

template<typename T> class MyClass 
{ 
public: 
    static constexpr const std::array<T,4> ARRAY() { return {4, 3, 1, 5}; }; 
}; 

int main() 
{ 
    constexpr std::array<int, 4> my_array(MyClass<int>::ARRAY()); // works fine -> can use the ARRAY to initialize constexpr std::array 

    constexpr int VALUE = 5 * MyClass<int>::ARRAY()[0]; // works also fine 

    int value; 
    value = my_array[0]; // can assign from constexpr 
    value = MyClass<int>::ARRAY()[0]; // undefined reference to `MyClass<int>::ARRAY 

    std::cout << VALUE << std::endl; 
    std::cout << value << std::endl; 

    return 0; 
} 

risultati attesi:

20 
4 
+0

Thx! Ho capito bene, che l'operatore pedice avrebbe funzionato per i calcoli del tempo di compilazione ma non in fase di runtime? E questo è un difetto speciale di 'std :: array' o questa situazione appare più spesso (come dici tu, restituisci anche oggetti constexpr dalla funzione constexpr). Ed è più probabile che sia risolto o ha una ragione per essere così com'è? E infine: non è un difetto di progettazione ottenere l'array tramite una funzione? – marlam

+0

@marlam è stato corretto in C++ 17. È perché std :: array's operator [] non è constexpr nel caso mutabile (l'interfaccia è stata definita in C++ 11 e si sono dimenticati di aggiornarla in C++ 14 quando gli oggetti constexpr potevano essere mutabili). Quindi l'operatore subscript lavorerà in ogni caso per un array non-constexpr e funzionerà con un array const constexpr. Questa mancanza non è comune e le persone che scrivono oggetti constexpr come in C++ 14 (come me) sono consapevoli del fatto che possono essere mutabili –

Problemi correlati