enum vs constexpr pour les constantes statiques réelles à l'intérieur des classes

permettez-moi de commencer par énoncer mon intention. Dans les vieux jours (C++), nous aurions le code comme:

class C
{
public:
  enum {SOME_VALUE=27};
};

alors nous pourrions utiliser SOME_VALUE tout au long de notre code comme une constante de temps de compilation et partout où le compilateur verrait C::SOME_VALUE , il se contenterait d'insérer le littéral 27.

de nos jours, il semble plus acceptable de changer ce code en quelque chose comme:

class C
{
public:
  static constexpr int SOME_VALUE=27;
};

cela semble beaucoup plus propre, donne SOME_VALUE un type bien défini et semble être l'approche préférée à partir de C++11. Le problème (imprévu au moins pour moi) est que cela provoque également des scénarios où SOME_VALUE doit être rendue externe. C'est, dans certains rpc fichier quelque part, nous devons ajouter:

constexpr int C::SOME_VALUE; // Now C::SOME_VALUE has external linkage

les cas qui causent cela semblent être lorsque les références const à SOME_VALUE sont utilisées, ce qui arrive très souvent dans le code de bibliothèque Standard C++ (Voir l'exemple au bas de cette question). Je j'utilise gcc 4.7.2 comme compilateur.

en raison de ce dilemme, je suis forcé de revenir à la définition de SOME_VALUE en tant qu'enum (i.e., ancienne école) afin d'éviter d'avoir à ajouter une définition à un fichier RPC pour certaines, mais pas toutes mes variables statiques membres de constexpr. N'y a-t-il pas un moyen de dire au compilateur que constexpr int SOME_VALUE=27 signifie que SOME_VALUE doit être traité seulement comme une constante de temps de compilation et jamais un objet avec liaison externe? Si vous voyez une référence de const utilisée avec, créez un temporaire. Si vous voyez son adresse prise, générez une erreur de temps de compilation si c'est nécessaire, parce que c'est une constante de temps de compilation et rien de plus.

voici un code échantillon apparemment Bénin qui nous oblige à ajouter la définition de SOME_VALUE dans un fichier cpp (encore une fois, testé avec gcc 4.7.2):

#include <vector>

class C
{
public:
  static constexpr int SOME_VALUE=5;
};

int main()
{
  std::vector<int> iv;

  iv.push_back(C::SOME_VALUE); // Will cause an undefined reference error
                               // at link time, because the compiler isn't smart
                               // enough to treat C::SOME_VALUE as the literal 5
                               // even though it's obvious at compile time
}

ajouter la ligne suivante au code au fichier scope résoudra l'erreur:

constexpr int C::SOME_VALUE;
60
demandé sur jww 2014-04-04 20:02:15

6 réponses

pour rappel, la version static constexpr fonctionnera comme vous l'aviez prévu en C++17. De N4618 Annexe D. 1 [depr.static_constexpr] :

D. 1 Redeclaration de static constexpr les membres de données [depr.static_constexpr]

pour la compatibilité avec les normes internationales C++ antérieures, un constexpr membre de données statiques peut être redondant non déclaré en dehors de la classe sans initialiseur. Cette utilisation est déconseillé. [ exemple:

struct A {
 static constexpr int n = 5; // definition (declaration in C++ 2014)
};

constexpr int A::n; // redundant declaration (definition in C++ 2014)

- fin exemple ]

le texte de norme pertinent qui permet cela est N4618 9.2.3 [classe.statique.données] / 3 :

[...] Un élément de données statiques en ligne peut être défini dans la définition de la classe et peut spécifier un initialiseur . Si le membre est déclaré avec le spécificateur constexpr , il peut être non déclaré dans l'espace de noms scope sans initialiseur (cet usage est déprécié; voir D. 1). [...]

cela vient avec la même machine qui a introduit la non - constexpr version de la même chose, membres de données statiques en ligne .

struct A {
 static inline int n = 5; // definition (illegal in C++ 2014)
}; 

inline int A::n; // illegal
6
répondu TBBle 2017-05-23 11:46:50

vous avez trois options ici:

  1. si votre classe est un modèle, alors mettez la définition de membre statique dans l'en-tête lui-même. Compilateur est nécessaire pour l'identifier comme une seule définition parmi plusieurs unités de traduction (voir [basic.def.odr] / 5)

  2. si votre classe n'est pas un modèle, vous pouvez facilement la mettre dans le fichier source

  3. alternativement déclarer constexpr fonction membre statique getSomeValue():

    class C
    {
    public:
        static constexpr int getSomeValue() { return 27; }
    };
    
8
répondu apoorvkul 2014-04-27 04:00:04

j'irais avec la classe enum:

http://en.cppreference.com/w/cpp/language/enum

http://www.stroustrup.com/C++11FAQ.html#enum

à Partir du premier lien:

enum class Color { RED, GREEN=20, BLUE};
Color r = Color::BLUE;
switch(r) {
    case Color::RED : std::cout << "red\n"; break;
    case Color::GREEN : std::cout << "green\n"; break;
    case Color::BLUE : std::cout << "blue\n"; break;
}
// int n = r; // error: no scoped enum to int conversion
int n = static_cast<int>(r); // OK, n = 21
2
répondu Fernando A. Gómez F. 2014-04-28 18:53:22

de nos jours, la voie préférée est:

enum class : int C { SOME_VALUE = 5 };
1
répondu edwinc 2014-04-15 16:50:29

de la norme c++ N3797 S3.5/2-3

un nom est considéré comme ayant un lien lorsqu'il peut dénoter le même objet, référence, Fonction, type, gabarit, namespace ou valeur qu'un nom introduit par une déclaration dans une autre portée:

- Lorsqu'un nom a un lien externe , l'entité qu'il désigne peut être désignée par des noms provenant d'autres unités de traduction ou d'autres unités de la même unité de traduction.

- Lorsqu'un nom a un lien interne , l'entité qu'il dénote peut être désignée par des noms d'autres domaines dans la même unité de traduction.

- Lorsqu'un nom n'a pas de lien , l'entité qu'il désigne ne peut pas être désignée par des noms d'autres domaines.

un nom ayant une portée d'espace de noms (3.3.6) a un lien interne s'il s'agit du nom de

- une variable, une fonction ou un modèle de fonction qui est explicitement déclaré statique; ou

- une variable non volatile qui est explicitement déclarée const ou constexpr et qui n'est ni déclarée extern ni déclarée antérieurement comme ayant une liaison externe; ou

- une donnée membre d'un syndicat anonyme.

ma lecture est que dans le code suivant:

public:
  static constexpr int SOME_VALUE=5;
  constexpr int SOME_VALUE=5;
};
static constexpr int SOME_VALUE=5;
constexpr int SOME_VALUE=5;

les 4 cas de SOME_VALUE ont un lien interne. Ils devraient être en lien avec une référence à SOME_VALUE dans la même unité de traduction et ne pas être visible ailleurs.

Évidemment, la première est une déclaration et non une définition. Il a besoin d'une définition au sein de la même unité de traduction. Si GCC le dit et MSVC ne le fait pas, alors MSVC a tort.

pour remplacer un enum, le numéro 2 devrait fonctionner. Il y a toujours un lien interne sans le mot clé static .

[révisé en réponse au commentaire]

1
répondu david.pfx 2014-04-16 23:17:52

vous pouvez le faire

class C
{
public:
  static const int SOME_VALUE=5;
};

int main()
{
  std::vector<int> iv;
  iv.push_back(C::SOME_VALUE); 
}

ce n'est même pas C++11, juste c++98

-1
répondu Ophir Gvirtzer 2014-04-07 16:21:51