2012-01-16 4 views
19

C'est une question assez simple mais je ne comprends pas pourquoi le code ci-dessous ne compile pas sur GCC 4.6.1. Il ne compile sur VS 2008 avec SP1:Erreur de compilation étrange de GCC (exemple simple inclus)

#include <iostream> 

class MyClass 
{ 
public: 
    const static int MinValue = -1000; 
    const static int MaxValue = 1000; 
}; 

void printValue(int i) 
{ 
    std::cout << i << std::endl; 
} 

int main(int argc, char** argv) 
{ 
    printValue(MyClass::MinValue); 
    printValue(MyClass::MaxValue); 
    printValue(argc < 42 ? MyClass::MinValue : MyClass::MaxValue); //This line gives the error 
} 

GCC dit:

[email protected]:~/temp$ g++ test.cpp 
/tmp/ccN2b95G.o: In function `main': 
test.cpp:(.text+0x54): undefined reference to `MyClass::MinValue' 
test.cpp:(.text+0x5c): undefined reference to `MyClass::MaxValue' 
collect2: ld returned 1 exit status 

Cependant, si je prends le troisième appel à 'printValue' il construit et fonctionne correctement. Donc c'est quelque chose à voir avec le '?' opérateur ... n'est-il pas valide de l'utiliser comme ça? De plus, si je remplace le 'argc < 42' par 'vrai' ou 'faux', il se construit également très bien.

Des idées ?!

+1

Bogue du compilateur? –

+2

@VJovic: Non - il doit y avoir une définition si elle est * odr-used *, c'est-à-dire "sauf si c'est un objet qui satisfait aux exigences pour apparaître dans une expression constante et la conversion lvalue-to-rvalue est immédiatement appliquée" . C'est le cas pour les arguments de la fonction, mais pas pour le résultat de l'opérateur conditionnel lorsque la condition n'est pas une expression constante et que le résultat est un lvalue. –

+1

@MikeSeymour: Je pense avoir lu des courriels à propos d'odr-used et des opérateurs ternaires, cela vous dérangerait-il de développer une bonne réponse? J'aimerais vraiment comprendre la subtilité ici. –

Répondre

7

Vous devez définir vos membres statiques en dehors de la déclaration de classe:

class MyClass 
{ 
public: 
    const static int MinValue; 
    const static int MaxValue; 
}; 


//implementation file 
const int MyClass::MinValue = -1000; 
const int MyClass::MaxValue = 1000; 
+1

cela n'explique pas pourquoi la dernière 'printValue' échoue. – greatwolf

+4

En fait, je pensais que cela ne s'applique qu'aux types non-intégrales? Ou suis-je en train de le confondre avec autre chose. Aussi, pourquoi les deux premiers appels à printValue compilent? – PolyVox

+0

@VictorT. c'est le cas, car vous essayez d'accéder à un membre statique défini de manière invalide. –

0

Je vais sortir sur un membre et que c'est probablement un bug du compilateur avec la version que vous utilisez. Comme AzzA l'a noté dans son commentaire, gcc-4.5.1 semble bien le construire ainsi que gcc-4.3.4. Je viens aussi de tester cela avec CLang 2.9 et cela accepte aussi le code. Comme alternative, vous pouvez définir vos valeurs min/max avec enums plutôt que const statique. Cela permettra d'économiser de la frappe:

#include <iostream> 

class MyClass 
{ 
public: 
    enum { MinValue = -1000, MaxValue = 1000 }; 
}; 

void printValue(const int i) 
{ 
    std::cout << i << std::endl; 
} 

int main(int argc, char** argv) 
{ 
    printValue(MyClass::MinValue); 
    printValue(MyClass::MaxValue); 
    printValue(argc < 42 ? MyClass::MinValue : MyClass::MaxValue); 
} 
+0

+1 Oui, j'ai vérifié sur GCC 4.3.4 aussi il y a 5 minutes. Plutôt interessant. – lapk

+0

Initialement, j'ai supposé que c'était un bug de compilateur mais, étant donné l'explication de Mike Seymour, il semble que ce soit correct. Au lieu de cela gcc-4.5.1 et 4.3.4 acceptent le code invalide, signifiant qu'il y avait un bogue qui a maintenant été corrigé? – PolyVox

+0

Pour référence, Clang 3.0 accepte le code. –

18

Selon le « Une définition la règle », une variable doit avoir exactement une définition si est est ODR utilisé. Ceci est défini par la norme 11 C++:

3,2/2 Une fonction variable ou non surchargé dont le nom apparaît comme une expression potentiellement évaluée est odr utilisée que si elle est un objet qui satisfait aux exigences de apparaissant dans une expression constante et la conversion lvalue-à-valeur est immédiatement appliquée.

et l'ODR lui-même:

3,2/3 Chaque programme contient exactement une définition de toutes les fonctions non-ligne ou variable qui est ODR-utilisé dans ce programme; aucun diagnostic requis.

Comme arguments d'appel de la fonction, ils ne sont pas ODR utilisé: ils sont des constantes avec une valeur spécifiée dans leur déclaration, et peuvent ainsi apparaître dans une expression constante; et ils sont passés en valeur, et sont donc immédiatement convertis en valeurs.

Ce n'est pas le cas lorsqu'ils sont utilisés dans l'expression conditionnelle. Étant donné que les deux sont lvalues ​​référence au même type, le résultat de l'expression conditionnelle sera une lvalue, selon l'une des règles assez compliquées définissant l'opérateur conditionnel:

5,16/4 Si les deuxième et troisième opérandes sont Glvalues ​​de la même catégorie de valeur et ayant le même type, le résultat est de ce type et de la catégorie de valeur.

(Cette règle autorise des expressions telles que (a?b:c)=d.Par conséquent, les constantes elles-mêmes ne sont pas immédiatement converties en rvalues ​​et l'expression conditionnelle ne peut pas apparaître dans une expression constante en raison de la condition d'exécution; par conséquent, ils sont odr-utilisés, et ont donc besoin d'une définition.

Comme vous le constatez, la modification de la condition en une expression constante corrige l'erreur de liaison; Cela changerait le type d'une constante. Mais l'expression telle qu'elle se présente exige qu'ils aient une définition dans une (et une seule) unité de traduction:

const int MyClass::MinValue; 
const int MyClass::MaxValue; 
+0

Merci Mike. Curieusement, Clang 3.0 accepte toujours le code. –

+0

+1 Bonne info. Sur une note de côté, je vais dépenser toute mon allocation «+ 1» sur les réponses à cette question ... J'espère, il n'y aura pas une autre réponse informative, que je devrai '+ 1 'par équité. – lapk

+0

Vous pourriez ajouter que les violations de la règle de définition unique sont «aucun diagnostic requis» (un concept que la norme ne semble pas définir, mais qui conduit inévitablement à un comportement indéfini, puisque le standard «omet la description de toute définition explicite de comportement ») –

Questions connexes