Le problème de langue étrange est CWG 1581 :
L'article 15 [spécial] est parfaitement clair que les fonctions membres spéciales ne sont définies implicitement que lorsqu'elles sont utilisées. Cela crée un problème pour les expressions constantes dans des contextes non évalués:
struct duration {
constexpr duration() {}
constexpr operator int() const { return 0; }
};
// duration d = duration(); // #1
int n = sizeof(short{duration(duration())});
Le problème ici est que nous ne sommes pas autorisés à définir implicitement constexpr duration::duration(duration&&)
dans ce programme, donc l'expression dans la liste d'initialisation n'est pas une expression constante (car elle appelle une fonction constexpr qui n'a pas été définie), donc l'initialiseur contreventé contient une conversion rétrécie , donc le programme est mal formé.
Si nous décommentons la ligne n ° 1, le constructeur de déplacement est implicitement défini et le programme est valide. Cette action effrayante à distance est extrêmement regrettable. Les implémentations divergent sur ce point.
Vous pouvez lire le reste de la description du problème.
Une résolution pour ce problème a été adoptée en P0859 à Albuquerque en 2017 (après l'expédition de C ++ 17). Ce problème était un bloqueur pour à la fois avoir un constexpr std::swap
(résolu dans P0879 ) et un constexpr std::invoke
(résolu dans P1065 , qui contient également des exemples CWG1581), tous deux pour C ++ 20.
L'exemple le plus simple à comprendre ici, à mon avis, est le code du rapport de bogue LLVM indiqué dans 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 est tout quand constexpr fonctions membres sont définies, et la résolution assure qu'ils sont définis uniquement lorsqu'ils sont utilisés. Après P0859, ce qui précède est bien formé (le type de b
est int
).
Étant donné std::swap
que les std::invoke
deux doivent s'appuyer sur la vérification des fonctions des membres (construction / affectation de déplacement dans le premier et opérateur d'appel / appels de substitution dans le second), ils dépendent tous deux de la résolution de ce problème.