Lo strano problema del linguaggio è CWG 1581 :
La clausola 15 [special] è perfettamente chiara che le funzioni speciali dei membri sono definite implicitamente solo quando vengono utilizzate in modo errato. Questo crea un problema per le espressioni costanti in contesti non valutati:
struct duration {
constexpr duration() {}
constexpr operator int() const { return 0; }
};
// duration d = duration(); // #1
int n = sizeof(short{duration(duration())});
Il problema qui è che non ci è permesso di definire implicitamente constexpr duration::duration(duration&&)
in questo programma, quindi l'espressione nell'elenco di inizializzatori non è un'espressione costante (perché invoca una funzione constexpr che non è stata definita), quindi l'inizializzatore rinforzato contiene una conversione restrittiva , quindi il programma è mal formato.
Se decommentiamo la riga n. 1, il costruttore di mosse è implicitamente definito e il programma è valido. Questa azione inquietante a distanza è estremamente sfortunata. Le implementazioni differiscono su questo punto.
Puoi leggere il resto della descrizione del problema.
Una risoluzione per questo problema è stata adottata in P0859 ad Albuquerque nel 2017 (dopo la spedizione di C ++ 17). Quel problema era un blocco sia per poter avere un constexpr std::swap
(risolto in P0879 ) sia un constexpr std::invoke
(risolto in P1065 , che ha anche esempi CWG1581), entrambi per C ++ 20.
L'esempio più semplice da comprendere qui, a mio avviso, è il codice del rapporto bug LLVM indicato in P1065:
template<typename T>
int f(T x)
{
return x.get();
}
template<typename T>
constexpr int g(T x)
{
return x.get();
}
int main() {
// O.K. The body of `f' is not required.
decltype(f(0)) a;
// Seems to instantiate the body of `g'
// and results in an error.
decltype(g(0)) b;
return 0;
}
CWG1581 riguarda tutto quando vengono definite le funzioni membro constexpr e la risoluzione garantisce che siano definite solo quando utilizzate. Dopo P0859, quanto sopra è ben formato (il tipo di b
è int
).
Poiché std::swap
ed std::invoke
entrambi hanno invocare verifica di funzioni membro (costruzione spostare / assegnazione nel primo e l'operatore di chiamata / chiamate surrogati in quest'ultimo), entrambi dipendevano la risoluzione di questo problema.