web-dev-qa-db-fra.com

Définition de membres entiers statiques constants dans la définition de classe

Si j'ai bien compris, C++ permet de définir des membres const const dans une classe, à condition qu'il s'agisse d'un type entier.

Pourquoi, alors, le code suivant me donne-t-il une erreur de l'éditeur de liens?

#include <algorithm>
#include <iostream>

class test
{
public:
    static const int N = 10;
};

int main()
{
    std::cout << test::N << "\n";
    std::min(9, test::N);
}

L'erreur que je reçois est:

test.cpp:(.text+0x130): undefined reference to `test::N'
collect2: ld returned 1 exit status

Fait intéressant, si je commente l'appel à std :: min, le code se compile et les liens se résument parfaitement (même si test :: N est également référencé à la ligne précédente).

Une idée de ce qui se passe?

Mon compilateur est gcc 4.4 sur Linux.

98
HighCommander4

Si j'ai bien compris, C++ permet de définir des membres const statiques dans une classe, à condition qu'il s'agisse d'un type entier.

Vous êtes en quelque sorte correct. Vous êtes autorisé à initialiser des intégrales constantes statiques dans la déclaration de classe, mais ce n'est pas une définition.

http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/cplr038.htm

Fait intéressant, si je commente l'appel de std :: min, le code se compile et les liens se résument parfaitement (même si test :: N est également référencé à la ligne précédente).

Une idée de ce qui se passe?

std :: min prend ses paramètres par référence const. Si vous les preniez par valeur, vous n’auriez pas ce problème, mais puisque vous avez besoin d’une référence, vous avez également besoin d’une définition.

Voici le chapitre/vers:

9.4.2/4 - Si un membre de données static est de type énumération const intégrale ou const, sa déclaration dans la définition de la classe peut spécifier initialiseur constant qui doit être une expression constante intégrale (5.19). Dans ce cas, le membre peut apparaître dans des expressions constantes intégrales. Le membre doit toujours être défini dans une portée d’espace de nommage s’il est utilisé dans le programme et la définition de la portée d’espace de nommage ne doit pas contenir initialiseur.

Voir la réponse de Chu pour une solution de contournement possible.

66
Edward Strange

L'exemple de Bjarne Stroustrup dans sa FAQ C++ suggère que vous avez raison et que vous n'avez besoin d'une définition que si vous prenez l'adresse.

class AE {
    // ...
public:
    static const int c6 = 7;
    static const int c7 = 31;
};

const int AE::c7;   // definition

int f()
{
    const int* p1 = &AE::c6;    // error: c6 not an lvalue
    const int* p2 = &AE::c7;    // ok
    // ...
}

Il dit "Vous pouvez prendre l'adresse d'un membre statique si (et seulement si) il a une définition hors classe". Ce qui suggère que cela fonctionnerait autrement. Peut-être que votre fonction min appelle des adresses en coulisses.

45
HostileFork

Une autre façon de faire cela, pour les types entiers de toute façon, est de définir des constantes comme des énumérations dans la classe:

class test
{
public:
    enum { N = 10 };
};
23
Stephen Chu

Pas seulement int. Mais vous ne pouvez pas définir la valeur dans la déclaration de classe. Si tu as:

class classname
{
    public:
       static int const N;
}

dans le fichier .h, vous devez avoir:

int const classname::N = 10;

dans le fichier .cpp.

10
Amardeep AC9MF

Voici une autre façon de contourner le problème:

std::min(9, int(test::N));

(Je pense que la réponse de Crazy Eddie explique correctement pourquoi le problème existe.)

9
karadoc

A partir de C++ 11, vous pouvez utiliser:

static constexpr int N = 10;

Cela nécessite théoriquement encore de définir la constante dans un fichier .cpp, mais tant que vous ne prenez pas l'adresse de N, il est très peu probable qu'une implémentation du compilateur produise une erreur;).

5
Carlo Wood

C++ permet de définir des membres const statiques dans une classe

Nope, 3.1 §2 dit:

ne déclaration est une définition sauf si elle déclare une fonction sans spécifier le corps de la fonction (8.4), elle contient le spécificateur extern (7.1.1) ou une spécification de liaison (7.5) et ni un initialiseur ni un initialiseur. functionbody, il déclare un membre de données statique dans une définition de classe (9.4), il s'agit d'une déclaration de nom de classe (9.1), d'une déclaration opaque-enum (7.2) ou d'un typedef déclaration (7.1.3), une déclaration d'utilisation (7.3.3) ou une directive d'utilisation (7.3.4).

3
fredoverflow