28 votes

Pourquoi appeler une fonction constexpr avec un tableau de membres n'est pas une expression constante ?

J'ai la fonction d'aide suivante :

template<typename T, std::size_t N>
constexpr std::size_t Length(const T(&)[N]) {
    return N;
}

Qui renvoie la longueur d'un tableau statique. Dans le passé, cela a toujours fonctionné, mais lorsque je fais ceci :

struct Foo
{
    unsigned int temp1[3];
    void Bar()
    {
        constexpr std::size_t t = Length(temp1); // Error here
    }
};

Je reçois une erreur lorsque j'utilise MSVS 2017 :

error C2131: expression did not evaluate to a constant

note: failure was caused by a read of a variable outside its lifetime

note: see usage of 'this'

J'espérais que quelqu'un pourrait m'éclairer sur ce que je fais mal.

0 votes

Sans rapport, mais tu pourrais aussi bien faire Length noexcept Il n'y a aucun scénario raisonnable dans lequel une exception pourrait être levée.

1 votes

Il semble que cela fonctionne bien sur gcc .

1 votes

Ça ressemble à un bug de MSVC pour moi. Cela devrait être une expression constante, je ne vois pas pourquoi cela ne le serait pas.

21voto

Rakete1111 Points 10248

MSVC est correct. Length(temp1) n'est pas une expression constante. De [expr.const]p2

Une expression e est une expression constante de base, sauf si l'évaluation de e en suivant les règles de la machine abstraite, évaluerait l'une des expressions suivantes :

  • this sauf dans une fonction constexpr ou un constructeur constexpr qui est évalué dans le cadre d'un processus d'évaluation. e ;

temp1 évalue this implicitement (parce que vous faites référence à this->temp1 ), et donc vous n'avez pas d'expression constante. gcc et clang l'acceptent car ils supportent les VLA comme une extension (essayez de compiler avec -Werror=vla o -pedantic-errors ).

Pourquoi n'est-ce pas autorisé ? Eh bien, vous pourriez accéder aux éléments sous-jacents et potentiellement les modifier. C'est tout à fait acceptable si vous avez affaire à un fichier constexpr ou un tableau qui est évalué comme une expression constante, mais si ce n'est pas le cas, vous ne pouvez pas avoir d'expression constante car vous manipulez des valeurs qui sont définies au moment de l'exécution.

4 votes

Si vous voulez tester la validité standard du code avec GCC, alors il faut contourner ses nombreuses extensions en utilisant des méthodes de test ciblées. -Werror=vla n'aurait guère de sens. La combinaison appropriée de drapeaux dans de tels cas est -std=... y -pedantic-errors .

5 votes

@AnT Bien que je sois tout à fait d'accord pour dire que la désactivation des extensions est généralement la meilleure solution, nous étions spécifiquement parler des VLA ici, alors j'ai pensé que je devais juste mentionner cette option d'avertissement ( -Wvla ) notamment. De plus, parfois, se débarrasser des extensions dans une base de code doit se faire une extension à la fois pour être pratique.

0 votes

Votre réponse semble plutôt détournée... Je pense que la façon directe de répondre à cette question serait "le compilateur ne tient pas compte du fait que vous ne tenez pas compte des valeurs réelles des éléments, qui ne sont pas des expressions constantes".

4voto

Mi-He Points 41
Length(decltype(temp1){})

semble fonctionner .

Malheureusement, je ne peux pas faire de commentaires, mais la solution de Mehrdad est fausse. La raison : ce n'est pas techniquement comportement indéfini mais il es comportement non défini. Pendant l'évaluation de constexpr, le compilateur doit attraper le comportement non défini. Par conséquent, le code est mal formé .

1 votes

Le code est mal formé, et comme le montre mon lien ci-dessus : gcc le rejette. Donc, il n'y a pas d'extension.

1voto

Mehrdad Points 70493

La réponse à votre question a déjà été donnée, mais pour ce qui est de la façon de "réparer" le problème, une méthode rapide et simple consiste à remplacer les éléments suivants

Length(temp1)

avec

Length(*(true ? NULL : &temp1))

ce qui, je pense, est techniquement comportement non défini mais qui va pratiquement fonctionner correctement pour MSVC.

Si vous avez besoin d'une solution qui fonctionne malgré l'UB, vous pouvez modifier Length pour utiliser un pointeur :

template<typename T, std::size_t N>
constexpr std::size_t Length(const T(*)[N]) {
    return N;
}

et ensuite vous pouvez utiliser Length(true ? NULL : &temp1) .

0 votes

C'est plus une extension que l'UB ? Parce que UB, dans une expression constante, entraîne des erreurs de forme, que l'implémentation peut contourner avec une extension.

0 votes

@Rakete1111 : Cela semble plausible mais je ne sais vraiment pas. J'essayais juste de fournir une solution de contournement au cas où quelqu'un aurait besoin de faire fonctionner le code (que j'ai testé pour MSVC).

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