31 votes

Pourquoi un si constexpr ne fait-il pas disparaître cette erreur d'expression constante?

En référence à cette question. La base de la constante de l'expression qui est utilisée pour initialiser l' constexpr variable y est mal formé. Beaucoup est une.

Mais si j'essaie de tourner la if en if constexpr:

template <typename T>
void foo() {
    constexpr int x = -1;
    if constexpr (x >= 0){
        constexpr int y = 1 << x;
    }
}

int main(){
    foo<int>();
}

L'erreur persiste. Avec GCC 7.2 encore donner:

error: right operand of shift expression '(1 << -1)' is negative [-fpermissive]

Mais j'ai pensé que la sémantique chèque doit être à gauche unpreformed sur un jeté de la branche.

Faire une indirection via un constexpr lambda ne l'aide, cependant:

template <typename T>
void foo(){
    constexpr int x = -1;
    constexpr auto p = []() constexpr { return x; };
    if constexpr (x >= 0){
        constexpr int y = 1<<p();
    }
}

L' constexpr spécificateur sur y semble modifier la façon dont les rebuts de la branche est cochée. Est-ce le comportement voulu?


@max66 a eu la gentillesse de vérifier d'autres implémentations. Il signale que l'erreur est reproductible avec GCC (7.2.0 / Tête 8.0.0) et Clang (5.0.0 / Tête 6.0.0).

20voto

Dietmar Kühl Points 70604

Le standard ne veut pas en dire beaucoup sur la jetée déclaration de l' if constexpr. Il y a essentiellement deux états [stmt.si] a propos de ces derniers:

  1. Dans un modèle enveloppant écarté les déclarations ne sont pas instanciés.
  2. Les noms référencés à partir d'une jetée de déclaration ne sont pas tenus de RLL être défini.

Aucune de ces s'applique à votre utilisation: les compilateurs sont corrects pour se plaindre de la constexpr si l'initialisation. Notez que vous aurez besoin pour faire de l'état dépend d'un paramètre de modèle lorsque vous voulez prendre avantage de l' instanciation à l'échec: si la valeur n'est pas dépendant d'un paramètre du modèle de l'échec se produit lorsque le modèle est défini. Par exemple, ce code ne fonctionne toujours:

template <typename T>
void f() {
    constexpr int x = -1;
    if constexpr (x >= 0){
        constexpr int y = 1<<x;
    }
}

Toutefois, si vous effectuez x dépend du type T c'est OK, même lorsqu' f est instancié avec int:

template <typename T>
void f() {
    constexpr T x = -1;
    if constexpr (x >= 0){
        constexpr int y = 1<<x;
    }
}
int main() {
    f<int>();
}

11voto

songyuanyao Points 2265

Notez que pour l'instruction ignorée par Constexpr Si :

la déclaration écartée ne peut pas être mal formée pour toutes les spécialisations possibles:

Pour résoudre le problème, vous pouvez définir l’instruction en fonction du paramètre de modèle, par exemple:

 template<typename T, int X> struct dependent_value { constexpr static int V = X; };

template <typename T>
void foo() {
    constexpr int x = -1;
    if constexpr (x >= 0){
        constexpr int y = 1 << dependent_value<T, x>::V;
    }
}
 

VIVRE

6voto

Kerrek SB Points 194696

Je ne sais pas pourquoi vous vous attendez à ce que la branche ne soit pas vérifiée. La seule fois où une branche if est "non cochée", c'est quand elle fait partie d'un modèle et n'est pas instanciée , comme indiqué dans [stmt.if] p2:

Lors de l'instanciation d'une entité modèle incluse (clause 17), si la condition n'est pas dépendante de la valeur après son instanciation, la sous-déclaration supprimée (le cas échéant) n'est pas instanciée.

Votre code ne semble pas être dans une situation où cela s'applique.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X