web-dev-qa-db-fra.com

std :: variante de modification dans constexpr

Considérez les deux programmes suivants:

#include<variant>
#include<iostream>

constexpr auto f() {
    using T = std::variant<bool, int>;
    T t(false);
    t = T(true);
    return std::get<bool>(t);
}

template<auto V> 
void print() { std::cout << V << "\n"; }

int main() {
    print<f()>();
}

et

#include<variant>
#include<iostream>

constexpr auto f() {
    using T = std::variant<bool, int>;
    T t(false);
    t = T(42);
    return std::get<int>(t);
}

template<auto V> 
void print() { std::cout << V << "\n"; }

int main() {
    print<f()>();
}

GCC compile ces deux éléments et génère les résultats escomptés. Clang ne les compile pas avec le message d'erreur suivant dans les deux cas:

<source>:4:16: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
constexpr auto f() {
               ^
<source>:7:7: note: non-constexpr function 'operator=' cannot be used in a constant expression
    t = T(42);
      ^
/opt/compiler-Explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/variant:1095:16: note: declared here
      variant& operator=(variant&&) = default;

Les deux programmes sont-ils bien formés? Sinon pourquoi?

De plus, s'ils ne sont pas bien formés, le message d'erreur que Clang donne est-il approprié? Selon [variant.assign] , l'opérateur d'affectation de déplacement doit être constexpr.

De plus, conformément à (7.4) , l'affectation du deuxième exemple doit se comporter de manière équivalente à emplace<int>(...) qui n'est pas déclarée constexpr ( [variant.mod] ). Cela implique-t-il que le deuxième exemple est mal formé, car l'argument de modèle ne peut pas être évalué comme une expression constante ou la formulation autorise-t-elle ce comportement?

MODIFIER:

D'après les commentaires, il semble que Clang compile et renvoie les résultats corrects si libc ++ est utilisé et que l'erreur se produit uniquement avec libstdc ++. Est-ce une incompatibilité entre la bibliothèque standard et le compilateur?

Sur https://godbolt.org/ :

Fonctionne dans les deux cas:

  • GCC 8.2.0 "-std = c ++ 17"
  • Cloisons 7.0.0 "-std = c ++ 17 -stdlib = libc ++"

Ne fonctionne pas dans les deux cas:

  • Clang 7.0.0 "-std = c ++ 17"
7
user10605163

Cela ressemble à un bogue de clang nous pouvons voir dans l'en-tête de variante de libstdc ++ que l'opérateur d'affectation de déplacement n'est en effet pas marqué constexpr:

variant& operator=(variant&&) = default;

mais un opérateur d’affectation de mouvement par défaut et défini implicitement peut toujours être constexpr, nous pouvons le voir à partir de [class.copy.assign] p10 (emphase mine):

Opérateur d’affectation copie/déplacement pour une classe X par défaut et non défini comme supprimé est défini implicitement lorsque odr-used ([basic.def.odr]) (par exemple, quand il est sélectionné par résolution de surcharge pour affecter à un objet de son type de classe), quand il est nécessaire pour constante evaluation ([expr.const]), ou lorsqu'il est explicitement configuré par défaut après sa première déclaration. L'affectation de copie/déplacement définie implicitement l'opérateur est constexpr si

  • (10.1) X est un type littéral, et 
  • (10.2) l'opérateur d'affectation sélectionné pour copier/déplacer chaque sous-objet de classe de base directe est une fonction constexpr, et 
  • (10.3) pour chaque membre de données non statique de X qui est de type classe (ou un tableau de celle-ci), l'opérateur d'affectation sélectionné pour copier/déplacer ce membre est une fonction constexpr.

D'après ce que je peux dire, l'implémentation de libstdc ++ devrait convenir à tous ces cas, il s'agit d'un type littéral, elle ne comporte pas de données membres non statiques et l'opérateur d'affectation pour toutes ses bases doit également être constexpr.

2
Shafik Yaghmour