71 votes

Pourquoi ne puis-je pas avoir un membre statique non-intégral dans une classe?

J'ai remarqué que C++ ne peut pas se compiler les suivantes:

class No_Good {
  static double const d = 1.0;
};

Toutefois, il sera heureux de permettre une variation où le double est changé pour un int, unsigned, ou n'importe quel type intégral:

class Happy_Times {
  static unsigned const u = 1;
};

Ma solution a été de le modifier pour lire:

class Now_Good {
  static double d() { return 1.0; }
};

et la figure que le compilateur va être assez intelligent pour inline si nécessaire... mais il m'a laissé de curieux.

Pourquoi le C++ concepteur(s) permettez-moi de static const int ou unsigned, mais pas un double?

Edit: je suis à l'aide de visual studio 7.1 (.net 2003) sur Windows XP.

Edit2:

La Question a été posée, mais pour l'exécution, l'erreur, j'ai vu:

error C2864: 'd' : only const static integral data members can be initialized inside a class or struct

48voto

Adam Rosenfield Points 176408

Le problème est que, avec un nombre entier, le compilateur généralement de ne jamais créer une adresse en mémoire de la constante. Il n'existe pas au moment de l'exécution, et chaque utilisation est incorporé dans le code environnant. Il peut encore décider de lui donner un emplacement de mémoire - si son adresse est déjà pris (ou s'il est passé par const référence à une fonction), qu'il doit. Afin de donner une adresse, il doit être défini dans certains de l'unité de traduction. Et dans ce cas, vous avez besoin de séparer la déclaration de la définition, car sinon il serait d'obtenir défini dans plusieurs unités de traduction.

À l'aide de g++ sans optimisation (-O0), elle est automatiquement inlines constante des variables de type entier mais pas constant, les valeurs doubles. À la hausse des niveaux d'optimisation (par exemple, -O1), il inlines constante doubles. Ainsi, le code suivant compile en -O1 , mais PAS à l' -O0:

// File a.h
class X
{
 public:
  static const double d = 1.0;
};

void foo(void);

// File a.cc
#include <stdio.h>

#include "a.h"

int main(void)
{
  foo();
  printf("%g\n", X::d);

  return 0;
}

// File b.cc
#include <stdio.h>

#include "a.h"

void foo(void)
{
  printf("foo: %g\n", X::d);
}

Ligne de commande:

g++ a.cc b.cc -O0 -o a   # Linker error: ld: undefined symbols: X::d
g++ a.cc b.cc -O1 -o a   # Succeeds

Pour un maximum de portabilité, vous devez déclarer vos variables dans les fichiers d'en-tête et de le définir une fois dans certains fichier source. Sans optimisation, ce ne sera pas nuire à la performance, puisque vous n'êtes pas à l'optimisation de toute façon, mais avec les optimisations activées, ce qui peut nuire à la performance, car le compilateur ne peut plus inline ces constantes dans d'autres fichiers source, sauf si vous activez l'option "tout le programme d'optimisation".

18voto

Je ne vois aucune raison technique

struct type {
    static const double value = 3.14;
};

est interdit. N'importe quelle occasion vous de trouver l'endroit où il fonctionne, c'est en raison de la non-portable définie par l'implémentation de fonctionnalités. Ils semblent également très limitée. Pour l'intégrale des constantes initialisé dans les définitions de classe, vous pouvez les utiliser et de les transmettre à des modèles non-arguments de type, et de les utiliser comme la taille de la matrice de dimensions. Mais vous ne pouvez pas le faire pour virgule flottante constantes. Permettant à virgule flottante paramètres du modèle apporterait son propre ensemble de règles qui ne sont pas vraiment la peine.

Néanmoins, la prochaine version C++ autorise l'utilisation de constexpr:

struct type {
    static constexpr double value = 3.14;
    static constexpr double value_as_function() { return 3.14; }
};

Et feront type::value une expression constante. Dans l'intervalle, votre meilleur pari est de suivre le modèle, également utilisée par std::numeric_limits:

struct type {
    static double value() { return 3.14; }
};

Il ne sera pas de retour une expression constante (la valeur n'est pas connue au moment de la compilation), mais que seules les questions théoriques, puisque la pratique, la valeur est insérée de toute façon. Voir la constexpr proposition. Il contient

4.4

Floating-point constant expressions

Traditionnellement, l'évaluation de virgule flottante expression constante à au moment de la compilation est une question épineuse. Pour l'homogénéité et de la généralité, nous vous suggérons de pour permettre constant de données d'expression de virgule flottante types, initialisé avec tout à virgule flottante constante les expressions. Qui permettra également d'augmenter la compatibilité avec C99 [ISO99, §6.6] ce qui permet

[#5] Une expression qui correspond à une constante est nécessaire dans plusieurs les contextes. Si une expression est flottant évalué dans la traduction envi- négatif sur l'environnement, la précision arithmétique et plage doit être au moins aussi grande que si l'expression ont été évalués dans la l'environnement d'exécution.

7voto

Michael Burr Points 181287

Il ne donne pas vraiment une justification, mais voici ce que Stroustrup a à dire à ce sujet dans "Le Langage de Programmation C++ Troisième Édition":

10.4.6.2 Membre Constantes

Il est également possible d'initialiser un statique intégrale constante membre par l'ajout d'une constante de l'expression initialiseur de sa déclaration d'un membre. Par exemple:

class Curious {
    static const int c1 = 7;        // ok, but remember definition
    static int c2 = 11;             // error: not const
    const int c3 = 13;              // error: not static
    static const int c4 = f(17);    // error: in-class initializer not constant
    static const float c5 = 7.0;    // error: in-class not integral
    // ...
};

Cependant, une initialisé membre doit encore être (unique) défini quelque part, et l'initialiseur, ne peut pas être répété:

const int Curious::c1;  // necessary, but don't repeat initializer here

Je considère cela comme un misfeature. Lorsque vous avez besoin d'une constante symbolique dans une déclaration de classe, utiliser un énumérateur (4.8, 14.4.6, 15.3). Pour exemple:

class X {
    enum { c1 = 7, c2 = 11, c3 = 13, c4 = 17 };
    // ...
};

De cette façon, aucun membre définition est nécessaire d'ailleurs, et vous n'êtes pas tenté de déclarer les variables, les nombres à virgule flottante, etc.

Et dans l'Annexe C (détails techniques) dans la Section C. 5 (Expressions Constantes), Stroustrup a ceci à dire à propos de "des expressions constantes":

Dans des endroits tels que les limites du tableau (5.2), les étiquettes de caisse (6.3.2), et les initialiseurs pour les agents recenseurs (4.8), C++ nécessite une expression constante. Une expression constante évalue à intégrante ou constante d'énumération. Une telle expression est composé de littéraux (4.3.1, 4.4.1, 4.5.1), les agents recenseurs (4.8), et consts initialisé par des expressions constantes. Dans un modèle, un modèle entier paramètre peut également être utilisé (C. 13.3). Flottant littéraux (4.5.1) peut être utilisé uniquement si explicitement converti intégrante type. Les fonctions, les objets de la classe, les pointeurs et références peuvent être utilisés comme opérandes sizeof opérateur (6.2).

Intuitivement, des expressions constantes sont des expressions simples qui peut être évaluée par le compilateur avant le programme est lié (9.1) et commence à courir.

Notez qu'il à peu près les feuilles flottantes point d'être capable de jouer dans 'expressions constantes'. Je soupçonne que virgule flottante a été laissé en dehors de ces types d'expressions constantes tout simplement parce qu'ils ne sont pas "simples" assez.

4voto

Mark Ransom Points 132545

Je ne sais pas pourquoi cela traiterait un double différent d'un int. Je pensais avoir déjà utilisé ce formulaire. Voici une solution de rechange:

 class Now_Better
{
    static double const d;
};
 

Et dans votre fichier .cpp:

 double const Now_Better::d = 1.0;
 

-1voto

Liu Nick Points 1

voici ma compréhension sur la base de Stroustrup à propos de dans la définition de la classe

Une classe est généralement déclarée dans un fichier d'en-tête et un fichier d'en-tête est généralement inclus dans de nombreuses unités de traduction. Toutefois, afin d'éviter compliqué linker règles, C++ exige que chaque objet est unique définition. Cette règle serait brisé si C++ admis en classe définition des entités qui doivent être stockées dans la mémoire comme des objets.

http://www.stroustrup.com/bs_faq2.html#in-class

donc, fondamentalement, ce n'est pas autorisé parce que le C++ ne le permettent pas. Afin de rendre les règles de l'éditeur de liens de plus simple, C++ exige que chaque objet a une définition unique.

membre statique n'a qu'un seul exemple dans le domaine de la classe, pas comme des variables statiques utilisés massivement dans C, qui n'a qu'un seul instatnce à l'intérieur d'une unité de traduction.

Si le membre statique est définie dans la classe, et la définition de la classe seront inclus dans de nombreuses unités de traduction, de sorte que l'éditeur de liens a faire plus de travail pour décider quel membre statique doit être utilisé en une seule par toutes les unités de traduction.

Mais pour les variables statiques, ils ne peuvent être utilisés à l'intérieur d'une unité de traduction, même dans le cas de différentes variables statiques dans l'unité de traduction différentes avec le même nom, elles ne touchent pas les uns les autres. Linker peut faire simple travail de lien régulier variables statiques à l'intérieur d'une unité de traduction.

afin de diminuer les complications et donner la fonction de base, C++ fournissent le seul dans la définition de la classe pour static const intégrale ou de type énumération.

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