OVH Cloud OVH Cloud

static const

7 réponses
Avatar
JBB
class TestStaticConst
{
public:
static const int C = 1;
};

cout << TestStaticConst::C << endl;

Cet exemple ne fonctionne pas avec Visual C++.
Je suis obligé de faire:

class TestStaticConst
{
public:
static const int C;
};

const int TestStaticConst::C = 1;

cout << TestStaticConst::C << endl;

Qui a raison ? ( d'apres la norme C++ si il y en a une).
Que preferez vous? (si vous avez le choix)

A part le fait de pouvoir mettre l'initialisation dans un cpp pour ne
pas avoir à recompiler trop de monde lors de la modification de C je
trouve ça moins lisible.
En plus j'ai pris la (bonne ou mauvaise ?) habitude de mettre mes
valeurs d'initialisation dans des .h




JBB

7 réponses

Avatar
Horst Kraemer
JBB wrote:

class TestStaticConst
{
public:
static const int C = 1;
};

cout << TestStaticConst::C << endl;

Cet exemple ne fonctionne pas avec Visual C++.


Parce que tu n'a pas défini C.

Je suis obligé de faire:

class TestStaticConst
{
public:
static const int C;
};

const int TestStaticConst::C = 1;

cout << TestStaticConst::C << endl;


ou bien

class TestStaticConst
{
public:
static const int C = 3;
int a[C];
};

const int TestStaticConst::C; // sans initialisation ici !!


cout << TestStaticConst::C << endl;

si tu veux utiliser la valeur de C dans la définition de la classe.

--
Horst

Avatar
kanze
JBB wrote:
class TestStaticConst
{
public:
static const int C = 1;
};

cout << TestStaticConst::C << endl;

Cet exemple ne fonctionne pas avec Visual C++.


Quelle version ? La possibilité de donner une valeur
d'initialisation dans la declaration (uniquement pour les static
const de type entier) a été ajoutée très tardivement dans la
norme, et n'est pas implémentée dans VC++ 6.0 (si je me souviens
bien).

Je suis obligé de faire:

class TestStaticConst
{
public:
static const int C;
};

const int TestStaticConst::C = 1;

cout << TestStaticConst::C << endl;


Même si tu donnes un initialisateur dans la declaration, il faut
une définition -- il faut que tu aies une

int const TestStaticConst::C ;

(sans initialisateur) quelque part. Sinon, c'est un comportement
indéfini (qui risque de marcher dans certains cas simples).

Sinon, la solution « classique », avant que la norme a permis
les initialisations comme ci-dessus, c'était un enum :

class TestStaticConst
{
public:
enum { C = 1 } ;
} ;

Ça marche aussi bien dans la plupart des cas, mais le type n'est
pas le même, ce qui pourrait provoquer des problèmes dans
l'instantiation des templates (fonctionTemplate(C) n'est pas la
même fonction que fonctionTemplate(1)) ou dans la résolution de
surcharge.

Qui a raison ? ( d'apres la norme C++ si il y en a une). Que
preferez vous? (si vous avez le choix)


J'ai encore pas mal du code avec les enum:-). Ça marche partout,
et je ne vois pas de raison à le changer. Dans de nouveau code,
si la portabilité vers d'anciens compilateurs n'est pas
démandée, j'utilise l'initialisation dans la declaration. Le
problème avec ton deuxième exemple, c'est que l'initialisation
n'est visible que dans une seule unité de compilation ;
l'expression n'est donc une constante que dans cette unité de
compilation.

Il y a une proposition devant le comité de permettre certaines
fonctions primitives dans des expressions constantes. Si elle
est adoptée (par les compilateurs, non seulement par le comité),
je crois que l'idéal serait :

class TestStaticConst
{
public:
static int C() { return 1 ; }
} ;

A part le fait de pouvoir mettre l'initialisation dans un cpp
pour ne pas avoir à recompiler trop de monde lors de la
modification de C je trouve ça moins lisible.


Tout dépend de ce que tu veux. Si C risque de changer souvent,
et que tu n'en as jamais besoin comme expression constante (par
exemple, comme dimension d'un tableau), alors, l'initialisation
dans une source est certainement la solution préférable.

En plus j'ai pris la (bonne ou mauvaise ?) habitude de mettre
mes valeurs d'initialisation dans des .h


Comment ? En règle générale, l'initialisation ne peut se trouver
que dans une définition, ou lors de l'instantiation. Et à
quelques exceptions près (les fonctions inline et les
templates), il ne doit y avoir qu'une seule définition dans le
programme. Mettre une définition dans un en-tête veut dire que
cet en-tête ne peut être inclu que par une seule unité de
compilation. Ce qui le rend vraiment peu utile.

En passant, je ne sais pas si tu connais bien cette distinction
entre définition et déclaration. Une déclaration fait en sort
que le compilateur connaît le nom ; une définition est une
déclaration, mais en plus, crée une instance de l'objet. À part
l'exception dont on parle ici, seulement une définition peut
comporter une initialisation. (C'est normal ; c'est l'instance
qu'on initialise.) À l'intérieur d'une classe, il n'y a que des
déclarations, au moins en ce qui concerne les données. Pour les
données membres non statique, il n'y a jamais de définition ;
l'instance n'est créé qu'en créant une instance de la classe, et
l'initialisation se fait par des initialisateur dans le
constructeur. Pour les données membres statique, en revanche, il
faut bien une définition quelque part pour en créer l'objet ;
c'est là, normalement, qu'on met l'initialisation aussi.

La seule exception à la règle, c'est quand le membre statique
est à la fois const, et de type entier. Dans ce cas-là, et ce
cas seulement, il est permis (non exigé) de donner
l'initialisation dans la declaration. Si on donne
l'initialisation dans la declaration, il faut 1) que
l'initialisation soit une expression entière constante, 2)
quelle soit identique dans toutes les unités de compilation, et
3) qu'il y a quand même une définition (sans initialisation)
quelque part.

Il n'y a pas d'autre mot à décrire cette exception que « hack ».
Elle est quand même bien pratique quand tu veux 1) que la
constante soit une constante entière (qui pourrait par exemple
servir de dimension de table, ou paramètre de template), et 2)
qu'il ait un type bien défini.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
kanze
JBB wrote:
class TestStaticConst
{
public:
static const int C = 1;
};

cout << TestStaticConst::C << endl;

Cet exemple ne fonctionne pas avec Visual C++.


Quelle version ? La possibilité de donner une valeur
d'initialisation dans la declaration (uniquement pour les static
const de type entier) a été ajoutée très tardivement dans la
norme, et n'est pas implémentée dans VC++ 6.0 (si je me souviens
bien).

Je suis obligé de faire:

class TestStaticConst
{
public:
static const int C;
};

const int TestStaticConst::C = 1;

cout << TestStaticConst::C << endl;


Même si tu donnes un initialisateur dans la declaration, il faut
une définition -- il faut que tu aies une

int const TestStaticConst::C ;

(sans initialisateur) quelque part. Sinon, c'est un comportement
indéfini (qui risque de marcher dans certains cas simples).

Sinon, la solution « classique », avant que la norme a permis
les initialisations comme ci-dessus, c'était un enum :

class TestStaticConst
{
public:
enum { C = 1 } ;
} ;

Ça marche aussi bien dans la plupart des cas, mais le type n'est
pas le même, ce qui pourrait provoquer des problèmes dans
l'instantiation des templates (fonctionTemplate(C) n'est pas la
même fonction que fonctionTemplate(1)) ou dans la résolution de
surcharge.

Qui a raison ? ( d'apres la norme C++ si il y en a une). Que
preferez vous? (si vous avez le choix)


J'ai encore pas mal du code avec les enum:-). Ça marche partout,
et je ne vois pas de raison à le changer. Dans de nouveau code,
si la portabilité vers d'anciens compilateurs n'est pas
démandée, j'utilise l'initialisation dans la declaration. Le
problème avec ton deuxième exemple, c'est que l'initialisation
n'est visible que dans une seule unité de compilation ;
l'expression n'est donc une constante que dans cette unité de
compilation.

Il y a une proposition devant le comité de permettre certaines
fonctions primitives dans des expressions constantes. Si elle
est adoptée (par les compilateurs, non seulement par le comité),
je crois que l'idéal serait :

class TestStaticConst
{
public:
static int C() { return 1 ; }
} ;

A part le fait de pouvoir mettre l'initialisation dans un cpp
pour ne pas avoir à recompiler trop de monde lors de la
modification de C je trouve ça moins lisible.


Tout dépend de ce que tu veux. Si C risque de changer souvent,
et que tu n'en as jamais besoin comme expression constante (par
exemple, comme dimension d'un tableau), alors, l'initialisation
dans une source est certainement la solution préférable.

En plus j'ai pris la (bonne ou mauvaise ?) habitude de mettre
mes valeurs d'initialisation dans des .h


Comment ? En règle générale, l'initialisation ne peut se trouver
que dans une définition, ou lors de l'instantiation. Et à
quelques exceptions près (les fonctions inline et les
templates), il ne doit y avoir qu'une seule définition dans le
programme. Mettre une définition dans un en-tête veut dire que
cet en-tête ne peut être inclu que par une seule unité de
compilation. Ce qui le rend vraiment peu utile.

En passant, je ne sais pas si tu connais bien cette distinction
entre définition et déclaration. Une déclaration fait en sort
que le compilateur connaît le nom ; une définition est une
déclaration, mais en plus, crée une instance de l'objet. À part
l'exception dont on parle ici, seulement une définition peut
comporter une initialisation. (C'est normal ; c'est l'instance
qu'on initialise.) À l'intérieur d'une classe, il n'y a que des
déclarations, au moins en ce qui concerne les données. Pour les
données membres non statique, il n'y a jamais de définition ;
l'instance n'est créé qu'en créant une instance de la classe, et
l'initialisation se fait par des initialisateur dans le
constructeur. Pour les données membres statique, en revanche, il
faut bien une définition quelque part pour en créer l'objet ;
c'est là, normalement, qu'on met l'initialisation aussi.

La seule exception à la règle, c'est quand le membre statique
est à la fois const, et de type entier. Dans ce cas-là, et ce
cas seulement, il est permis (non exigé) de donner
l'initialisation dans la declaration. Si on donne
l'initialisation dans la declaration, il faut 1) que
l'initialisation soit une expression entière constante, 2)
quelle soit identique dans toutes les unités de compilation, et
3) qu'il y a quand même une définition (sans initialisation)
quelque part.

Il n'y a pas d'autre mot à décrire cette exception que « hack ».
Elle est quand même bien pratique quand tu veux 1) que la
constante soit une constante entière (qui pourrait par exemple
servir de dimension de table, ou paramètre de template), et 2)
qu'il ait un type bien défini.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
JBB
JBB wrote:

class TestStaticConst
{
public:
static const int C = 1;
};



cout << TestStaticConst::C << endl;



Cet exemple ne fonctionne pas avec Visual C++.



Quelle version ? La possibilité de donner une valeur
d'initialisation dans la declaration (uniquement pour les static
const de type entier) a été ajoutée très tardivement dans la
norme, et n'est pas implémentée dans VC++ 6.0 (si je me souviens
bien).

Effectivement c'est VC++ 6.0


Je suis obligé de faire:



class TestStaticConst
{
public:
static const int C;
};



const int TestStaticConst::C = 1;



cout << TestStaticConst::C << endl;



Même si tu donnes un initialisateur dans la declaration, il faut
une définition -- il faut que tu aies une

int const TestStaticConst::C ;

(sans initialisateur) quelque part. Sinon, c'est un comportement
indéfini (qui risque de marcher dans certains cas simples).

Avec GCC (devC++) j'ai l'impression que ca marche.

Est seulement une impression ?
Du moment que ma donnée est de type const rien n'empeche le compilateur
de la remplacer par sa valeur lors de la compilation. (non?)

Sinon, la solution « classique », avant que la norme a permis
les initialisations comme ci-dessus, c'était un enum :

class TestStaticConst
{
public:
enum { C = 1 } ;
} ;



Effectivement. Je me sers souvent d'enum mais pour une seule constante
ça me vient moins à l'idée.
D'ailleurs les MFC utilisent cette technique pour les IDD des CDialog.

Ça marche aussi bien dans la plupart des cas, mais le type n'est
pas le même, ce qui pourrait provoquer des problèmes dans
l'instantiation des templates (fonctionTemplate(C) n'est pas la
même fonction que fonctionTemplate(1)) ou dans la résolution de
surcharge.


Qui a raison ? ( d'apres la norme C++ si il y en a une). Que
preferez vous? (si vous avez le choix)



J'ai encore pas mal du code avec les enum:-). Ça marche partout,
et je ne vois pas de raison à le changer. Dans de nouveau code,
si la portabilité vers d'anciens compilateurs n'est pas
démandée, j'utilise l'initialisation dans la declaration. Le
problème avec ton deuxième exemple, c'est que l'initialisation
n'est visible que dans une seule unité de compilation ;
l'expression n'est donc une constante que dans cette unité de
compilation.

Il y a une proposition devant le comité de permettre certaines
fonctions primitives dans des expressions constantes. Si elle
est adoptée (par les compilateurs, non seulement par le comité),
je crois que l'idéal serait :

class TestStaticConst
{
public:
static int C() { return 1 ; }
} ;


A part le fait de pouvoir mettre l'initialisation dans un cpp
pour ne pas avoir à recompiler trop de monde lors de la
modification de C je trouve ça moins lisible.



Tout dépend de ce que tu veux. Si C risque de changer souvent,
et que tu n'en as jamais besoin comme expression constante (par
exemple, comme dimension d'un tableau), alors, l'initialisation
dans une source est certainement la solution préférable.


En plus j'ai pris la (bonne ou mauvaise ?) habitude de mettre
mes valeurs d'initialisation dans des .h


Pour faire ça j'utilise des #define pour toutes mes constantes. C'est ce

qu'on m'a appris.

Comment ? En règle générale, l'initialisation ne peut se trouver
que dans une définition, ou lors de l'instantiation. Et à
quelques exceptions près (les fonctions inline et les
templates), il ne doit y avoir qu'une seule définition dans le
programme. Mettre une définition dans un en-tête veut dire que
cet en-tête ne peut être inclu que par une seule unité de
compilation. Ce qui le rend vraiment peu utile.

En passant, je ne sais pas si tu connais bien cette distinction
entre définition et déclaration. Une déclaration fait en sort
que le compilateur connaît le nom ; une définition est une
déclaration, mais en plus, crée une instance de l'objet. À part
l'exception dont on parle ici, seulement une définition peut
comporter une initialisation. (C'est normal ; c'est l'instance
qu'on initialise.) À l'intérieur d'une classe, il n'y a que des
déclarations, au moins en ce qui concerne les données. Pour les
données membres non statique, il n'y a jamais de définition ;
l'instance n'est créé qu'en créant une instance de la classe, et
l'initialisation se fait par des initialisateur dans le
constructeur. Pour les données membres statique, en revanche, il
faut bien une définition quelque part pour en créer l'objet ;
c'est là, normalement, qu'on met l'initialisation aussi.

La seule exception à la règle, c'est quand le membre statique
est à la fois const, et de type entier. Dans ce cas-là, et ce
cas seulement, il est permis (non exigé) de donner
l'initialisation dans la declaration. Si on donne
l'initialisation dans la declaration, il faut 1) que
l'initialisation soit une expression entière constante, 2)
quelle soit identique dans toutes les unités de compilation, et
3) qu'il y a quand même une définition (sans initialisation)
quelque part.



Effectivement ça ne marche qu'avec des const int.(et char,short... tous
le type doit être 'integral' comme me dit le compilateur)
(j'aurais bien aimé qua ça marche aussi avec des char * :
const char * s ="chaine"; ).
Dans les autres cas on est obligé de separer définition et déclaration.

Il n'y a pas d'autre mot à décrire cette exception que « hack ».
Elle est quand même bien pratique quand tu veux 1) que la
constante soit une constante entière (qui pourrait par exemple
servir de dimension de table, ou paramètre de template), et 2)
qu'il ait un type bien défini.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34




Avatar
kanze
JBB wrote:
JBB wrote:



[...]
Même si tu donnes un initialisateur dans la declaration, il
faut une définition -- il faut que tu aies une

int const TestStaticConst::C ;

(sans initialisateur) quelque part. Sinon, c'est un
comportement indéfini (qui risque de marcher dans certains
cas simples).


Avec GCC (devC++) j'ai l'impression que ca marche. Est
seulement une impression ?


Formellement, si tu n'as pas la définition, c'est un
comportement indéfini. Pratiquement, on ne rencontre que deux
cas : le compilateur a besoin de l'instance (l'objet), et tu as
une erreur lors de l'édition des liens, ou il n'en a pas besoin,
et ça marche.

Du moment que ma donnée est de type const rien n'empeche le
compilateur de la remplacer par sa valeur lors de la
compilation. (non?)


Dans certains cas (dimension d'un tableau, par exemple), il en
est même obligé. Dans d'autres... Si tu en prends l'adresse, par
exemple, sans la définition, pas d'objet, et sans l'objet, pas
d'adresse. Et n'oublie pas qu'il peut y avoir des cas où le
compilateur a besoin de l'adresse même si tu ne le prends pas
explicitement (passage de paramètre par référence, par exemple
-- mettons comme paramètre à un vector<int>::push_back.)

Sinon, la solution « classique », avant que la norme a permis
les initialisations comme ci-dessus, c'était un enum :

class TestStaticConst
{
public:
enum { C = 1 } ;
} ;


Effectivement. Je me sers souvent d'enum mais pour une seule
constante ça me vient moins à l'idée. D'ailleurs les MFC
utilisent cette technique pour les IDD des CDialog.


Comme j'ai dit, c'était la solution classique ; tout le monde le
faisait. Elle a le dèsavantage que le type de la constante n'est
pas int ; c'est un type sans nom. Ça peut jouer des tours avec
la résolution du surcharge des fonctions, ou avec des templates
(mais je n'ai jamais eu de problème moi-même).

[...]
Tout dépend de ce que tu veux. Si C risque de changer
souvent, et que tu n'en as jamais besoin comme expression
constante (par exemple, comme dimension d'un tableau),
alors, l'initialisation dans une source est certainement la
solution préférable.

En plus j'ai pris la (bonne ou mauvaise ?) habitude de
mettre mes valeurs d'initialisation dans des .h



Pour faire ça j'utilise des #define pour toutes mes
constantes. C'est ce qu'on m'a appris.


Alors, il faut dèsapprendre. C'est la plus mauvaise solution.

[...]
La seule exception à la règle, c'est quand le membre
statique est à la fois const, et de type entier. Dans ce
cas-là, et ce cas seulement, il est permis (non exigé) de
donner l'initialisation dans la declaration. Si on donne
l'initialisation dans la declaration, il faut 1) que
l'initialisation soit une expression entière constante, 2)
quelle soit identique dans toutes les unités de compilation,
et 3) qu'il y a quand même une définition (sans
initialisation) quelque part.


Effectivement ça ne marche qu'avec des const int.(et
char,short... tous le type doit être 'integral' comme me dit
le compilateur) (j'aurais bien aimé qua ça marche aussi avec
des char * :
const char * s ="chaine"; ).
Dans les autres cas on est obligé de separer définition et
déclaration.


Comme j'ai dit ci-dessus, la définition est toujours
obligatoire. Et elle est toujours séparée de la déclaration : la
déclaration doit être dans la définition de la classe ; la
définition ne peut pas l'être. Dans une classe non templatée, la
définition doit se trouver en fait dans un fichier source, et
non dans un en-tête. Dans une classe templatée, dans l'absense
de export, la définition se trouvera bien aussi dans un en-tête,
mais toujours en dehors de la classe.

L'exception spéciale ici ne concerne que l'endroit où l'on
spécifie l'initialisation -- dans la déclaration à la place de
dans la définition. Et sa justification, c'est que les
expressions entière const joue un rôle spécial dans le langage,
et que dans la pratique, le compilateur a besoin de savoir la
valeur quand il s'en sert. Ce qui n'est pas le cas des autres
expressions constantes.

La règle générale respecte à peu près les règles de base de la
génie logicielle -- qu'on sépare nettement la partie
« contractuelle » dont dépend le code client de la partie
« implémentation », qui doit pouvoir évoluer sans mise à jour du
client. La séparation n'est pas parfait (les membres privés ne
fait pas partie du contrat, par exemple), et les mechanismes mis
en oeuvre pour l'obtenir (l'insertion physique de texte) ne sont
vraiment pas ce qu'on fait de mieux. (Les problèmes de la
séparation représente une compromis, à mon avis guère évitable
dans le contexte où le C++ a vu le jour. Les mechanismes
utilisés pour la séparation sont conditionnés historiquement
aussi.) De ce point de vue : l'existance de la constante fait
partie du contrat ; sa valeur, en revanche, normalement pas. Il
est donc normal que j'en spécifie l'existance dans la définition
de la classe (dans l'en-tête), mais que je n'en donne tous les
détails de l'instance dans une source (l'implémentation).

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
JBB
Et comment je peut faire un truc du genre ?

class Test
{
public:

static const int tailleTableau;

private:
int tableau[tailleTableau];
};

const int Test::tailleTableau = 100;


A part faire une allocation dynamique, ou un template.
Avatar
kanze
JBB wrote:
Et comment je peut faire un truc du genre ?

class Test
{
public:

static const int tailleTableau;

private:
int tableau[tailleTableau];
};

const int Test::tailleTableau = 100;


class Test
{
public:
static int const tailleTableau = 100 ;

private:
int tableau[ tailleTableau ] ;
} ;

avec un compilateur moderne. Sinon :

class Test
{
public:
enum { tailleTableau = 100 } ;

private:
int tableau[ tailleTableau ] ;
} ;

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34