web-dev-qa-db-fra.com

Utilisation de variable de membre de const statique C ++

Disons que j'ai une classe qui nécessite quelques constantes pour fonctionner. Plusieurs fonctions membres nécessitent l'utilisation de ces constantes. L'utilisation de #define est mal vue car elle peut provoquer des collisions. Les constantes sont des motifs hexadécimaux de 8 ou 16 bits et sont stockées sous la forme uint8_t ou uint16_t. Ces constantes ne changent pas non plus d'instance en instance de la classe, et donc la mémoire (bien que très peu de mémoire) peut être sauvegardée en ayant une seule copie des constantes.

Y a-t-il quelque chose de inapproprié, ou peut-être de meilleure façon d'accomplir ce qui précède au lieu de simplement faire quelque chose comme ceci:

// mycode.h
// .......
class myclass {
private:
  static const uint16_t kMyClassConstant_ = 0xBEEF;
// .......
};

Merci d'avance pour l'aide.

25
It'sPete

Compte tenu de votre description de la situation, je dirais en utilisant static const membres est une bonne approche. En C++ 11, vous souhaiterez peut-être le changer en static constexpr pour souligner qu'il s'agit d'une constante au moment de la compilation, bien que rien ne changera effectivement en conséquence.

Si vous faites référence à myclass::kMyClassContant_ quelque part dans le code d'une manière qui est pertinente selon la règle d'une définition (odr), esp. dans les contextes qui nécessitent une référence (y compris const-reference), le compilateur se plaindra qu'il n'y a pas de définition de la constante. Le simple fait de le déclarer et de l'initialiser dans la classe n'est pas suffisant dans ce cas. Cela peut vous obliger à séparer la déclaration et la définition:

// mycode.h
class myclass {
private:
  static const uint16_t kMyClassConstant_;
};

// mycode.cpp
const uint16_t myclass::kMyClassConstant_ = 0xBEEF;

Pour éviter d'avoir à gérer des déclarations et des définitions distinctes, certaines personnes préfèrent déclarer une fonction constexpr en ligne plutôt qu'une variable réelle:

// mycode.h
class myclass {
private:
  static constexpr uint16_t kMyClassConstant_()
  { return 0xBEEF; }
};

Il s'agit d'une solution de contournement correcte pour de nombreux problèmes liés à l'ODR, et il n'entraîne aucune perte de performances. Son utilité dépend de la charge que représente le maintien de déclarations et définitions distinctes d'une constante statique ordinaire. Si vous vous attendez à ce que vos constantes ne changent jamais à mesure que votre code évolue, il est préférable d'utiliser des constantes statiques ordinaires avec des définitions distinctes. Mais si vous modifiez fréquemment les définitions des constantes, le fait de devoir recompiler le fichier de définition et de le lier à nouveau à toutes les parties pertinentes du projet peut vous faire considérer la solution basée sur les fonctions ci-dessus comme une meilleure alternative.

Un dernier commentaire sur le type de données: le forcer à 16 bits en utilisant std::uint16_t peut être utile si vous devez stocker de nombreuses valeurs sous une forme compacte. Sinon, la taille réelle peut ne pas vraiment avoir d'importance, auquel cas std::uint_fast16_t (qui peut être supérieur à 16 bits) peut être préférable.

44
jogojapan

Vous pouvez utiliser des traits de type pour implémenter ceci:

#include <type_traits>

class myclass {
private:
  typedef std::integral_constant<uint16_t , 0xBEEF> kMyClassConstant;

  // ...
};

utilisé comme myclass::kMyClassConstant::value.

Cela montre le but de la mise en œuvre d'une constante intégrale et vous empêche de prendre accidentellement une adresse de la constante.

5
Jan Herrmann

Depuis C++ 17, nous avons accès aux variables inline, qui s'occupent des problèmes liés aux odr. Plusieurs options:

// mycode.h
class myclass {
    static const inline uint16_t kMyClassConstant_ = 0xBEEF;
};

Ou, s'il peut être marqué constexpr (comme dans ce cas):

// mycode.h
class myclass {
    static constexpr inline uint16_t kMyClassConstant_ = 0xBEEF;
};

Ce qui peut être simplifié pour:

// mycode.h
class myclass {
    static constexpr uint16_t kMyClassConstant_ = 0xBEEF;
};

Parce qu'en C++ 17 constexpr implique inline pour static membres de données.

4
Acorn