Qu'est-ce qui détermine si une fonction constexpr est une expression constante?

Nov 30 2020

(le compilateur utilisé est gcc avec c ++ 17 pour autant que je sache (difficile à trouver dans Visual Studio))

#include <iostream>

using namespace std;

void increment( int& v )
{
    ++v;
}

int constexpr f()
{
    int v = 0;
    increment( v );
    return v;
}

int main( )
{
    cout << f( ) << '\n';
}

Le code ci-dessus donne l'erreur lors de la compilation:

La fonction constexpr 'f' ne peut pas donner une expression constante.

Si je comprends bien, c'est parce que la fonction incrementn'est pas une constexpr. Ce qui me trouble, c'est que le code suivant se compile correctement:

#include <iostream>

using namespace std;

void increment( int& v )
{
    ++v;
}

int constexpr f()
{
    int v = 0;
    for( int i = 0; i < 1; ++i )
    {
        increment( v );
    }   
    return v;
}

int main( )
{
    cout << f( ) << '\n';
}

Ce code est fonctionnellement le même et il compile, même si l'incrément n'est toujours pas une constexpr. Je ne comprends pas comment il est possible qu'une boucle for à travers la plage [0, 1) ffasse réaliser au compilateur que la fonction est en fait une constexpr.

Si quelqu'un peut donner un aperçu de constexpr en c ++ et de cette apparente incohérence, je l'apprécierais beaucoup.

Réponses

12 aschepler Nov 30 2020 at 22:23

Les deux programmes sont "mal formés aucun diagnostic requis", par [dcl.constexpr] / 6 :

Pour une fonction constexpr ou un constructeur constexpr qui n'est ni par défaut ni un modèle, si aucune valeur d'argument n'existe de telle sorte qu'un appel de la fonction ou du constructeur pourrait être une sous-expression évaluée d'une expression constante de base, ou, pour un constructeur, une sous-expression évaluée de l'initialisation de l'expression complète d'un objet à initialisation constante ( [basic.start.static] ), le programme est mal formé, aucun diagnostic n'est requis.

C'est un peu étrange que gcc ne remarque tout simplement pas le problème avec le deuxième programme, mais il est toujours conforme.

Notez qu'un diagnostic serait nécessaire s'il fétait utilisé dans un contexte qui nécessite en fait une expression constante, par exemple constexpr int n = f();.

Certaines choses ne sont jamais autorisées dans une fonction constexpr. Celles-ci nécessitent un diagnostic (généralement un message d'erreur), même si la fonction n'est jamais utilisée dans une expression constante - voir la réponse de cigien . Mais les programmes en question ne violent aucune de ces règles plus strictes.

6 cigien Nov 30 2020 at 22:27

Puisque vous n'appelez pas fune expression constante, votre question est de savoir si le compilateur est requis pour diagnostiquer qui fne peut pas être appelé dans une expression constante, en se basant uniquement sur sa définition .

Les exigences relatives à la définition d'une constexprfonction sont énumérées ici :

La définition d'une fonction constexpr doit satisfaire aux exigences suivantes:

(3.1) son type de retour (le cas échéant) doit être un type littéral;

(3.2) chacun de ses types de paramètres doit être un type littéral;

(3.3) il ne doit pas être une coroutine;

(3.4) si la fonction est un constructeur ou un destructeur, sa classe ne doit avoir aucune classe de base virtuelle;

(3.5) son corps de fonction ne doit pas contenir

(3.5.1) une instruction goto,

(3.5.2) une étiquette d'identification,

(3.5.3) une définition d'une variable de type non littéral ou de durée de stockage statique ou de thread.

Comme on peut le voir, la définition de fne viole aucune des exigences de la liste. Ainsi, un compilateur est conforme s'il choisit de ne pas diagnostiquer cela.

Comme indiqué dans la réponse d'Aschepler , des constexprfonctions comme fcelle-ci ne peuvent pas être appelées dans une expression constante, mais ne peuvent pas être diagnostiquées en tant que telles, sont considérées comme mal formées, sans diagnostic.

2 MarshallClow Nov 30 2020 at 22:22

Vous n'êtes pas réellement "appelant" fau moment de la compilation.

si votre fonction principale incluait: static_assert(f() == 1, "f() returned 1");Je suppose que vous obtiendrez une erreur "f () n'est pas une expression constante".

Voici une question connexe

Sneftel Nov 30 2020 at 22:12

La norme exige qu'une constexprfonction soit réellement évaluable au moment de la compilation pour certains ensembles de paramètres mais pas tous. Il ne nécessite pas que les compilateurs diagnostiquent une constexprfonction faisant certaines choses qui pourraient ne pas être à la compilation dans certaines circonstances, ni même si une telle fonction a un tel ensemble de paramètres. Cela leur évite d'avoir à résoudre le problème de l'arrêt.