129 votes

Définition des membres statiques const integer dans la définition de la classe

D'après ce que j'ai compris, le C++ permet de définir des membres constants statiques à l'intérieur d'une classe, à condition qu'il s'agisse d'un type d'entier.

Pourquoi, alors, le code suivant me donne-t-il une erreur d'édition 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 j'obtiens est la suivante :

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

Il est intéressant de noter que si je commente l'appel à std::min, le code se compile et se lie 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 sous Linux.

4 votes

Fonctionne parfaitement avec Visual Studio 2010.

4 votes

Cette erreur exacte est expliquée à l'adresse suivante gcc.gnu.org/wiki/

0 votes

Dans le cas particulier de char vous pouvez le définir comme suit constexpr static const char &N = "n"[0]; . Notez que les & . Je suppose que cela fonctionne parce que les chaînes littérales sont définies automatiquement. Je suis un peu inquiet à ce sujet cependant - cela pourrait se comporter étrangement dans un fichier d'en-tête parmi différentes unités de traduction, car la chaîne de caractères sera probablement à plusieurs adresses différentes.

80voto

Crazy Eddie Points 23778

D'après ce que j'ai compris, le C++ permet de définir des membres constants statiques à l'intérieur d'une classe, à condition qu'il s'agisse d'un type d'entier.

Vous avez en quelque sorte raison. Vous êtes autorisé à initialiser des intégrales statiques constantes dans la déclaration de la classe, mais il ne s'agit pas d'une définition.

Il est intéressant de noter que si je commente l'appel à std::min, le code se compile et se lie 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 constante. S'il les prenait par valeur, vous n'auriez pas ce problème, mais comme vous avez besoin d'une référence, vous avez aussi besoin d'une définition.

Voici le chapitre/verset :

9.4.2/4 - Si un static est un membre de const intégrale ou const sa déclaration dans la définition de la classe peut spécifier un type d'énumération. initialisateur constant qui doit être une expression intégrale constante (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 noms s'il est utilisé dans le programme et la définition de la portée d'espace de noms ne doit pas contenir d'expression constante intégrale. initialisateur .

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

0 votes

Je vois, c'est intéressant. Dans ce cas, quelle est la différence entre fournir la valeur au moment de la déclaration et fournir la valeur au moment de la définition ? Laquelle est recommandée ?

0 votes

Je pense que l'on peut se passer d'une définition tant que l'on n'utilise pas la variable. Si vous ne l'utilisez qu'en tant que partie d'une expression constante, alors la variable n'est jamais utilisée. Sinon, il ne semble pas y avoir de grande différence, si ce n'est que l'on peut voir la valeur dans l'en-tête, ce qui n'est pas forcément ce que l'on veut.

2 votes

La réponse la plus brève est static const x=1 ; est une valeur r mais pas une valeur l. La valeur est disponible en tant que constante au moment de la compilation. La valeur est disponible en tant que constante au moment de la compilation (vous pouvez dimensionner un tableau avec) static const y ; [no initializer] doit être définie dans un fichier cpp et peut être utilisée soit comme valeur r, soit comme valeur l.

62voto

HostileFork Points 14697

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 laisse supposer qu'il fonctionnerait autrement. Peut-être que votre fonction min invoque les adresses d'une manière ou d'une autre dans les coulisses.

2 votes

std::min prend ses paramètres par référence, c'est pourquoi une définition est nécessaire.

0 votes

Comment écrire la définition si AE est une classe template AE<classe T> et que c7 n'est pas un int mais un T::size_type ? J'ai initialisé la valeur à "-1" dans l'en-tête mais clang dit undefined value et je ne sais pas comment écrire la définition.

0 votes

@Fabian Je suis en voyage, au téléphone et un peu occupé... mais je pense que votre commentaire serait mieux rédigé sous la forme d'une nouvelle question. Rédigez une MCVE y compris l'erreur que vous obtenez, et peut-être aussi ce que dit gcc. Je parie que les gens vous diront rapidement ce qu'il en est.

26voto

Stephen Chu Points 8440

Une autre façon de procéder, en tout cas pour les types entiers, est de définir les constantes comme des enums dans la classe :

class test
{
public:
    enum { N = 10 };
};

2 votes

Cela résoudrait probablement le problème. Lorsque N est utilisé comme paramètre de min(), il entraîne la création d'une variable temporaire plutôt que d'essayer de faire référence à une variable supposée existante.

0 votes

L'avantage est qu'il peut être rendu privé.

14voto

Amardeep Points 10417

Pas seulement les int's. Mais vous ne pouvez pas définir la valeur dans la déclaration de la classe. Si vous avez :

class classname
{
    public:
       static int const N;
}

dans le fichier .h alors vous devez avoir :

int const classname::N = 10;

dans le fichier .cpp.

2 votes

Je sais que vous pouvez declara une variable de n'importe quel type à l'intérieur de la déclaration de la classe. J'ai dit que je pensais que les constantes statiques entières pouvaient également être défini dans la déclaration de la classe. Est-ce que ce n'est pas le cas ? Si ce n'est pas le cas, pourquoi le compilateur ne donne-t-il pas d'erreur à la ligne où j'essaie de le définir à l'intérieur de la classe ? De plus, pourquoi la ligne std::cout ne provoque-t-elle pas d'erreur du compilateur, alors que la ligne std::min en provoque une ?

0 votes

Non, il n'est pas possible de définir des membres statiques dans la déclaration de la classe, car l'initialisation émet du code. Contrairement à une fonction en ligne qui émet également du code, une définition statique est globalement unique.

0 votes

@HighCommander4 : Vous pouvez fournir un initialisateur pour la fonction static const membre intégral dans la définition de la classe. Mais cela n'empêche pas ne définit pas ce membre. Voir la réponse de Noah Roberts pour plus de détails.

9voto

karadoc Points 970

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 décrit correctement la raison pour laquelle le problème existe).

5 votes

Ou même std::min(9, +test::N);

0 votes

Mais la grande question est de savoir si tout cela est optimal. Je ne sais pas ce qu'il en est pour vous, mais ce qui m'attire le plus dans l'idée de sauter la définition, c'est qu'elle ne devrait pas occuper de mémoire et qu'il n'y a pas de surcharge liée à l'utilisation de const static.

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