Undefined reference to static const int
J'ai couru dans un problème intéressant aujourd'hui. Considérons cet exemple simple:
template <typename T>
void foo(const T & a) { /* code */ }
//This would also fail
//void foo(const int & a) { /* code */}
class Bar
{
public:
static const int kConst = 1;
void func()
{
foo(kConst); //This is the important line
}
};
int main()
{
Bar b;
b.func();
}
Lors de la compilation j'obtiens une erreur:
Undefined reference to 'Bar::kConst'
Maintenant, je suis assez sûr que c'est parce que la static const int
n'est défini nulle part, ce qui est intentionnel, car, selon ma compréhension, le compilateur doit être en mesure de faire le remplacement au moment de la compilation et pas besoin d'une définition. Toutefois, étant donné que la fonction prend un const int &
paramètre, il semble ne pas faire de la substitution, et préférant une référence. Je peux résoudre ce problème en faisant les changements suivants:
foo(static_cast<int>(kConst));
Je crois que c'est maintenant forcer le compilateur à faire un temporaire de type int, et puis passer une référence à ce qui peut réussir à faire au moment de la compilation.
Je me demandais si c'était intentionnel, ou suis-je en attendre trop de gcc pour être en mesure de gérer ce cas? Ou est-ce quelque chose que je ne devrais pas être en train de faire pour une raison quelconque?
const int kConst = 1;
avec le même résultat. Aussi, il y a rarement une raison (je ne peux penser à aucun) pour une fonction qui prend un paramètre de type const int &
- il suffit d'utiliser une int
ici.la fonction réelle a été un modèle, je vais modifier ma question de le mentionner.
pour info, ne pas le faire
static
donne une erreur "ISO C++ forbids l'initialisation de membre "kConst'...faire "kConst' static.'Mon mal, merci pour la correction.
L'ennuyeux, c'est que cette erreur peut s'afficher dans anodin utilise comme
std::min( some_val, kConst)
, depuis std::min<T>
a des paramètres de type T const &
, et la conséquence en est que nous avons besoin de passer une référence à le kConst. J'ai trouvé qu'il ne se produisait que lorsque l'optimisation est éteint. Fixe à l'aide d'une statique de la fonte.
OriginalL'auteur JaredC | 2011-03-22
Vous devez vous connecter pour publier un commentaire.
C'est intentionnel, 9.4.2/4 dit:
Lorsque vous passez à la donnée membre statique par référence const, vous "utilisation", 3.2/2:
Donc, en fait, vous "utilisez" quand vous passez par une valeur trop, ou dans un
static_cast
. C'est juste que GCC a vous laisser descendre le crochet dans un cas mais pas dans l'autre.[Edit: gcc est l'application des règles de C++0x brouillons: "Une variable ou non surchargé la fonction dont le nom apparaît comme potentiellement évalué l'expression est de rll-utilisé, sauf si c'est un objet qui satisfait aux exigences pour apparaître dans une expression constante (5.19) et la lvalue-à-rvalue de conversion (4.1) est immédiatement appliquée.". La statique de la fonte effectue lvalue-rvalue conversion immédiatement, donc dans C++0x ce n'est pas "utilisé".]
Le problème pratique de la const de référence est que
foo
est dans son droit de prendre l'adresse de son argument, et de la comparer par exemple avec l'adresse de l'argument à partir d'un autre appel, stockée dans un mondial. Depuis une donnée membre statique est un objet unique, cela signifie que si vous appelezfoo(kConst)
à partir de deux différents Cus, l'adresse de l'objet passé doit être la même dans chaque cas. Autant que je sache, GCC ne peut pas faire en sorte que, sauf si l'objet est défini dans un (et un seul) TU.OK, donc dans ce cas
foo
est un modèle, d'où la définition est visible dans tous les Syndicats, alors peut-être que le compilateur pourrait, en théorie, d'écarter le risque qu'il fait n'importe quoi avec l'adresse. Mais en général, vous avez certainement ne devriez pas prendre des adresses ou des références à des non-existant objets 😉Pour compléter la conformité, je pense que vous auriez à définir quelque chose comme
template <int N> int intvalue() { return N; }
. Puis avecintvalue<kConst>
,kConst
n'apparaît que dans un contexte exigeant intégrante expression constante, et donc n'est pas utilisé. Mais la fonction renvoie un temporaire avec la même valeur quekConst
, et qui peut se lier à une référence const. Je ne suis pas sûr, cependant, il pourrait y avoir une façon plus simple de les appliquer de façon portable quekConst
n'est pas utilisé.J'ai rencontrer le même problème en utilisant ces static const variable d'un opérateur ternaire (c'est à dire quelque chose comme
r = s ? kConst1 : kConst2
) avec gcc 4.7. Je l'ai résolu en utilisant une réelleif
. En tout cas merci pour la réponse!...et std::min / std::max, ce qui m'a amené ici!
"Autant que je sache, GCC ne peut pas faire en sorte que, sauf si l'objet est défini dans un (et un seul) TU". C'est dommage, car il est déjà possible de le faire avec des constantes de compilation, à plusieurs reprises comme "faible defs' dans .rodata, puis demandez à l'éditeur de liens en choisir un, ce qui garantit que tous les refs de il aurait la même adresse. C'est effectivement ce qui est fait pour typeid; il peut échouer dans bizarre façons, même si, quand elle est partagée libs sont utilisés.
OriginalL'auteur Steve Jessop
Si vous écrivez static const variable avec initialiseur à l'intérieur de la déclaration de classe, c'est juste que si vous avez écrit
et GCC va traiter de la même manière, ce qui signifie qu'il n'a pas une adresse.
Le code correct doit être
OriginalL'auteur pelya
C'est vraiment un cas valide. Surtout parce que foo pourrait être une fonction de la STL comme std::count qui prend un const T& comme troisième argument.
J'ai passé beaucoup de temps à essayer de comprendre pourquoi l'éditeur de liens a eu des problèmes avec un tel code de base.
Le message d'erreur
nous dit que le linker ne trouve pas un symbole.
Nous pouvons voir à partir de la 'U' cette Barre::kConst est pas défini. Par conséquent, lorsque l'éditeur de liens essaie de faire son travail, elle doit trouver le symbole. Mais vous ne déclarer kConst et de ne pas le définir.
La solution en C++ est aussi le définir comme suit:
Ensuite, vous pouvez voir que le compilateur va mettre la définition dans le générés fichier objet:
Maintenant, vous pouvez voir le " R " en disant qu'elle est définie dans la section de données.
Avez-vous un exemple ? Sur l'exemple fourni,
>nm -C main.o | grep kConst
donne-moi une seule ligne0000000000400644 R Bar::kConst
.Je le vois lors de la compilation d'une bibliothèque statique.
Dans ce cas, bien sûr ! Une bibliothèque statique n'est qu'un ensemble de fichiers de l'objet. Le lien n'est fait par le client de la lib statique. Donc, si vous mettez dans le fichier d'archive, le fichier de l'objet contenant la définition de la const et un autre fichier de l'objet appelant Bar::func(), vous verrez le symbole une fois à la définition et à la fois sans:
nm -C lib.a
vous donneConstants.o: 0000000000000000 R Bar::kConst
etmain_file.o: U Bar::kConst ...
.OriginalL'auteur Stac
g++ version 4.3.4 accepte ce code (voir ce lien). Mais g++ version 4.4.0 il rejette.
OriginalL'auteur TonyK
Vous pouvez aussi le remplacer par un constexpr fonction de membre:
OriginalL'auteur Ben-Uri
Je pense que cet artefact de C++ signifie que chaque fois que
Bar::kConst
est visé, sa valeur littérale est utilisé à la place.Cela signifie que dans la pratique, il n'y a pas de variable pour faire un point de référence.
Vous pourriez avoir à faire ceci:
foo(static_cast<int>(kConst));
, droit?OriginalL'auteur quamrana
Truc Simple: utiliser
+
avant lakConst
transmis à la fonction. Cela permettra d'éviter la constante de la prise d'une référence à partir de, et de cette façon, le code ne génère pas d'une demande de l'éditeur de liens à l'objet constant, mais il continuera avec le compilateur constante de temps de la valeur à la place.static const
valeur est initialisée à la déclaration. Ce serait toujours conduire à l'erreur de l'éditeur de liens, et lors de la même constante est également déclarée séparément dans un fichier objet, ce serait une erreur, trop. Le compilateur est aussi pleinement conscient de la situation.Quelle est la meilleure façon de refuser les références? Je suis actuellement en train de faire
static_cast<decltype(kConst)>(kConst)
.Je voudrais savoir comment faire cela aussi. Votre tatic_cast<decltype(kConst)>(kConst) astuce ne fonctionne pas dans le cas que kConst est un char[64]; il obtient " erreur: static_cast de 'char *' to 'decltype(start_time)' (aka 'char [64]") n'est pas autorisé".
Je ne suis pas dans le logiciel de l'archéologie, mais aussi loin que je me souviens c'est très dur pour passer les raw d'un tableau en fonction d'ici la copie. Donc, du point de vue syntaxique de la
foo()
de la question d'origine aura besoin de l'adresse, et il n'existe aucun mécanisme de la traiter comme une copie temporaire de l'ensemble du tableau.OriginalL'auteur Ethouris
J'ai eu le même problème, comme l'a mentionné Cloderic (static const dans un opérateur ternaire:
r = s ? kConst1 : kConst2
), mais il ne se plaint après la mise hors tension optimisation du compilateur (-O0 au lieu de -Os). Arrivé sur gcc-none-eabi 4.8.5.OriginalL'auteur Scg