Sono stato sorpreso di scoprire che GCC e Clang non sono d'accordo sul fatto di darmi un errore linker quando si passa un membro constexpr statico in base al valore quando non esiste una definizione fuori classe:Comportamento anomalo che passa a membri statici di constexpr senza definizioni in base al valore
#include <iostream>
#include <type_traits>
#include <typeinfo>
template <typename X>
void show(X)
{
std::cout << typeid(X).name() << std::endl;
}
template <typename T>
struct Foo
{
//static constexpr struct E {} nested {}; // works in gcc and clang
//static constexpr struct E {T x;} nested {}; // doesn't work in gcc
//static constexpr enum E {} nested {}; // works in gcc and clang
//static constexpr enum E { FOO } nested {}; // works in gcc and clang
//static constexpr struct E { constexpr E() {} constexpr E(const E&) {} T x=T();} nested {}; // works in gcc and clang
static constexpr struct E { constexpr E() {} constexpr E(const E&) = default; T x=T(); } nested {}; // doesn't work in gcc
};
int main()
{
Foo<int> x;
show(x.nested);
}
Lo snippet può essere riprodotto con here.
Vorrei utilizzare la sintassi del primo linea con nessun out-of-class definizione:
static constexpr struct E {} nested {}; // works in gcc and clang
Quando non ci sono utenti nel E
, Clang e GCC sembrano solo preoccuparsi che non ho out- definizione della classe di nested
se scatta ODR (ad esempio prendendo l'indirizzo). Questo standard è obbligatorio o fortunato?
Quando ci sono membri, GCC (5.2) sembra voler anche che io abbia definito manualmente un costruttore di copia di constexpr. Si tratta di un bug?
Da Google e SO ho trovato diverse risposte diverse ma è difficile distinguere quali sono aggiornati con C++ 14. In C++ 98/03 credo che solo i tipi interi possano essere inizializzati all'interno della classe. I penso a che C++ 14 lo ha esteso a tipi "letterali" che includono cose costruibili di constexpr. Non so se questo è lo stesso che dire che mi è permesso di cavarmela senza avere comunque una definizione fuori dalla classe.
Interessante. Il tuo caso di test finale funziona con gcc -O2 e clang -O3, ma non clang -O2 o gcc -O1. Anche il tuo secondo e il quinto test non funzionano a -0 su alcun compilatore. Tempi divertenti ... (inoltre, la parte di [basic.def.odr] su ciò che conta e non conta come un uso di odr mi fa male alla testa). –
Sì, non considererei il passare di valore come un odr-use, dal momento che non dovrebbe basarsi sull'identità dell'oggetto ... ma non ho un avvocato linguistico sufficiente per discernere. –
Nota che odr [le violazioni non richiedono una diagnostica] (http://stackoverflow.com/a/28446388/1708801), quindi entrambe possono essere corrette. La storia di come abbiamo ottenuto [le attuali regole odr è piuttosto distorta] (http://stackoverflow.com/q/31565836/1708801). Questo è un caso curioso. –