web-dev-qa-db-fra.com

constexpr if et static_assert

P0292R1 constexpr if a été inclus , sur la bonne voie pour C++ 17. Cela semble utile (et peut remplacer l'utilisation de SFINAE), mais un commentaire concernant static_assert étant mal formé, aucun diagnostic requis dans la fausse branche me fait peur:

Disarming static_assert declarations in the non-taken branch of a
constexpr if is not proposed.

void f() {
  if constexpr (false)
    static_assert(false);   // ill-formed
}

template<class T>
void g() {
  if constexpr (false)
    static_assert(false);   // ill-formed; no 
               // diagnostic required for template definition
}

Je suppose qu'il est totalement interdit d'utiliser static_assert à l'intérieur de constexpr if (au moins la branche false/non-take, mais cela signifie en pratique que ce n'est pas une chose sûre ou utile à faire).

Comment cela vient-il du texte standard? Je ne trouve aucune mention de static_assert dans la formulation de la proposition, et les fonctions constexpr C++ 14 permettent static_assert (détails sur cppreference: constexpr ).

Se cache-t-il dans cette nouvelle phrase (après 6.4.1)? :

Lorsqu'une instruction constexpr if apparaît dans une entité basée sur un modèle, lors d'une instanciation du modèle englobant ou du lambda générique, une instruction rejetée n'est pas instanciée.

À partir de là, je suppose qu'il est également interdit, aucun diagnostic requis, d'appeler d'autres fonctions constexpr (modèle) qui quelque part en bas du graphe d'appel peut appeler static_assert.

Conclusion:

Si ma compréhension est correcte, cela ne met-il pas une limite assez stricte à la sécurité et à l'utilité de constexpr if comme nous devrions le savoir (à partir de la documentation ou de l'inspection du code) sur toute utilisation de static_assert? Mes inquiétudes sont-elles déplacées?

Mise à jour:

Ce code se compile sans avertissement (clang head 3.9.0) mais à ma connaissance mal formé, aucun diagnostic requis. Valide ou non?

template< typename T>
constexpr void other_library_foo(){
    static_assert(std::is_same<T,int>::value);
}

template<class T>
void g() {
  if constexpr (false)
    other_library_foo<T>(); 
}

int main(){
    g<float>();
    g<int>();
}
29
Johan Lundberg

Il s'agit d'une règle bien établie pour les modèles - la même règle qui permet aux compilateurs de diagnostiquer template<class> void f() { return 1; }. [temp.res]/8 avec la nouvelle modification en gras:

Le programme est mal formé, aucun diagnostic requis, si:

  • aucune spécialisation valide ne peut être générée pour un modèle ou une sous-déclaration d'un constexpr if instruction ([stmt.if]) dans un modèle et le modèle n'est pas instancié, ou
  • [...]

Aucune spécialisation valide ne peut être générée pour un modèle contenant static_assert dont la condition n'est pas dépendante et est évaluée à false, le programme est donc un rapport de non-remise incorrect.

static_asserts avec une condition dépendante pouvant être évaluée à true pour au moins un type ne sont pas affectés.

24
T.C.

Edit: Je garde cette réponse avec des exemples et des explications plus détaillées sur les malentendus qui conduisent à ces questions. La réponse courte de T.C. est strictement suffisant.

Après avoir relu la proposition et sur static_assert Dans le version actuelle , je conclus que mes inquiétudes étaient erronées. Tout d'abord, l'accent devrait être mis ici sur le modèle définition.

mal formé; aucun diagnostic requis pour la définition du modèle

Si un modèle est instancié, tout static_assert Se déclenche comme prévu. Cela correspond probablement à la déclaration que j'ai citée:

... une instruction rejetée n'est pas instanciée.

C'est un peu vague pour moi, mais je conclus que cela signifie que modèles se produisant dans la déclaration rejetée ne sera pas instancié. Cependant, tout autre code doit être syntaxiquement valide. Une static_assert(F), [où F est faux, littéralement ou une valeur constexpr] à l'intérieur d'une clause if constexpr Supprimée sera donc toujours "mordante" lorsque le modèle contenant static_assert Est instancié. Ou (non requis, à la merci du compilateur) déjà à la déclaration s'il est connu pour toujours être faux.

Exemples: ( démo en direct )

#include <type_traits>

template< typename T>
constexpr void some_library_foo(){
    static_assert(std::is_same<T,int>::value);
}

template< typename T>
constexpr void other_library_bar(){
    static_assert(std::is_same<T,float>::value);
}

template< typename T>
constexpr void buzz(){
    // This template is ill-formated, (invalid) no diagnostic required,
    // since there are no T which could make it valid. (As also mentioned
    // in the answer by T.C.).
    // That also means that neither of these are required to fire, but
    // clang does (and very likely all compilers for similar cases), at
    // least when buzz is instantiated.
    static_assert(! std::is_same<T,T>::value);
    static_assert(false); // does fire already at declaration
                          // with latest version of clang
}

template<class T, bool IntCase>
void g() {
  if constexpr (IntCase){
    some_library_foo<T>();

    // Both two static asserts will fire even though within if constexpr:
    static_assert(!IntCase) ;  // ill-formated diagnostic required if 
                              // IntCase is true
    static_assert(IntCase) ; // ill-formated diagnostic required if 
                              // IntCase is false

    // However, don't do this:
    static_assert(false) ; // ill-formated, no diagnostic required, 
                           // for the same reasons as with buzz().

  } else {
    other_library_bar<T>();
  }      
}

int main(){
    g<int,true>();
    g<float,false>();

    //g<int,false>(); // ill-formated, diagnostic required
    //g<float,true>(); // ill-formated, diagnostic required
}

Le texte standard sur static_assert Est remarquablement court. En standard, c'est une façon de rendre le programme mal formé avec diagnostic (comme l'a également souligné @immibis):

7.6 ... Si la valeur de l'expression lorsqu'elle est ainsi convertie est vraie, la déclaration n'a aucun effet. Sinon, le programme est mal formé et le message de diagnostic résultant (1.4) doit inclure le texte du littéral de chaîne, s'il est fourni ...

8
Johan Lundberg