web-dev-qa-db-fra.com

Pourquoi n'est-ce pas une expression constante?

Dans cet exemple trivial, test2 ne parvient pas à compiler même si test1 réussit, et je ne vois pas pourquoi c'est le cas. Si arr[i] convient à une valeur de retour d'une fonction marquée constexpr alors pourquoi ne peut-il pas être utilisé comme argument de modèle non-type?

template<char c>
struct t
{ 
    static const char value = c;
};

template <unsigned N>
constexpr char test1(const char (&arr)[N], unsigned i)
{
    return arr[i];
}

template <unsigned N>
constexpr char test2(const char (&arr)[N], unsigned i)
{
    return t<arr[i]>::value;
}

int main()
{
   char a = test1("Test", 0); //Compiles OK
   char b = test2("Test", 0); //error: non-type template argument 
                              //is not a constant expression
}

Edit: Cela ne fait aucune différence:

template<char c>
struct t
{ 
    static const char value = c;
};

template <unsigned N>
constexpr char test1(const char (&arr)[N])
{
    return arr[0];
}

template <unsigned N>
constexpr char test2(const char (&arr)[N])
{
    return t<arr[0]>::value;
}

int main()
{
   char a = test1("Test"); //Compiles OK
   char b = test2("Test"); //error: non-type template argument 
                           //is not a constant expression
}
20
Chris_F

Réponse courte: il n'y a pas de paramètres de fonction constexpr dans C++11/14.

Réponse plus longue: dans test1(), si i n'est pas une constante au moment de la compilation, la fonction est toujours utilisable au moment de l'exécution. Mais dans test2(), le compilateur ne peut pas savoir si i est une constante de temps de compilation, et pourtant elle est requise pour que la fonction compile.

Par exemple. le code suivant pour test1 sera compilé

int i = 0;    
char a = test1("Test", i); // OK, runtime invocation of test1()

constexpr int i = 0;
constexpr char a = test1("Test", i); // also OK, compile time invocation of test1()

Disons simplement votre test2() pour

constexpr char test3(unsigned i)
{
    return t<i>::value;
}

Cela ne sera pas compilé pour test3(0) car à l'intérieur de test3(), il ne peut pas être prouvé que i est une expression de compilation inconditionnelle. Vous auriez besoin de paramètres de fonction constexpr pour pouvoir exprimer cela.

Citation de la norme

5.19 Expressions constantes [expr.const]

2 Une expression conditionnelle e est une expression constante de base à moins que l'évaluation de e, suivant les règles de la machine abstraite (1.9), n'évalue l'une des expressions suivantes:

- une expression id qui fait référence à une variable ou un membre de données de type référence, sauf si la référence a une initialisation précédente et
- il est initialisé avec une expression constante ou

- il s'agit d'un membre de données non statique d'un objet dont la durée de vie a commencé dans le cadre de l'évaluation de e;

Cette section contient l'exemple de code suivant correspondant à votre question:

constexpr int f1(int k) {
    constexpr int x = k; // error: x is not initialized by a
                         // constant expression because lifetime of k
                         // began outside the initializer of x
    return x;
}

Étant donné que x dans l'exemple ci-dessus n'est pas une expression constante, cela signifie que vous ne pouvez pas instancier de modèles avec x ou k dans f1.

22
TemplateRex

Il y a une idée fausse de ce que fait constexpr ici. Cela indique qu'une fonction doit être évaluable au moment de la compilation pour les arguments appropriés, mais elle pas supprime l'exigence de compilation dans le cas général.

Prenons la première version:

template <unsigned N>
constexpr char test1(const char (&arr)[N], unsigned i) {
    return arr[i];
}

Maintenant, il s'agit clairement d'une évaluation au moment de la compilation:

enum { CompileTimeConstant = test1("Test", 0) };

votre exemple peut être, mais c'est un problème d'optimiseur/QoI:

char MayBeCompileTimeConstant = test1("Test", 0);

et cet exemple ne l'est évidemment pas, mais est doit toujours être évaluable

char arr[10];
int i;
std::cin >> i;
std::cin >> arr;
char b = test1(arr, i);
std::cout << "'" << arr << "'[" << i << "] = " << b << '\n';

Puisque test2 ne peut pas être compilé pour le dernier cas, il ne peut pas du tout être compilé. (Veuillez noter que je ne suggère pas que le code est bon).

7
Useless

Le problème ici est que l'appel de arr[i] évoque l'opérateur d'indice operator[]. Cet opérateur ne retourne pas une expression constante.

Ce n'est pas un problème de constexpr en fait, c'est un problème de déduction d'argument de modèle. Un argument de modèle non type doit être une expression constante qui n'est pas l'argument de retour de l'opérateur d'indice.

Par conséquent, le compilateur se plaint à juste titre que arr[i] n'est pas une expression constante.

2
101010

Car arr[i] n'est pas une expression constante à la compilation. Il peut être différent au moment de l'exécution.

1
user1742529