clang: pas de ligne virtuel définitions de méthode (abstraite pure classe C++ )
j'essaie de compiler le code C++ simple suivant en utilisant Clang-3.5:
test.h:
class A
{
public:
A();
virtual ~A() = 0;
};
test.cc:
#include "test.h"
A::A() {;}
A::~A() {;}
la commande que j'utilise pour compiler ceci (Linux, uname-r: 3.16.0-4-amd64):
$clang-3.5 -Weverything -std=c++11 -c test.cc
et l'erreur que j'obtiens:
./test.h:1:7: warning: 'A' has no out-of-line virtual method definitions; its vtable will be emitted in every translation unit [-Wweak-vtables]
vous savez pourquoi cet avertissement est émis? Le destructeur virtuel n'est pas incorporé à tous. Tout à fait le ci-contre, il y a une définition hors de la ligne fournie dans test.cc. Qu'est-ce que je rate?
Modifier
Je ne pense pas que cette question soit une copie de :
que signifie clang's-Wweak-vtables?
comme L'a suggéré Filip Roseen. Dans ma question, je fais spécifiquement référence à des classes purement abstraites (non mentionnées dans le duplicata suggéré). Je sais comment -Wweak-vtables
fonctionne avec des classes non abstraites et Je suis bien avec elle. Dans mon exemple, j'ai défini le destructeur (qui est purement abstrait) dans le fichier d'implémentation. Cela devrait empêcher Clang d'émettre des erreurs, même avec -Wweak-vtables
.
3 réponses
nous ne voulons pas placer le vtable dans chaque unité de traduction. Il doit donc y avoir un certain ordre d'unités de traduction, de sorte que nous pouvons dire alors, que nous plaçons la table ronde dans la "première" unité de traduction. Si cet ordre n'est pas défini, nous émettons l'avertissement.
Vous trouvez la réponse dans la Itanium CXX ABI . Dans la section sur les tables virtuelles (5.2.3) vous trouverez:
La table virtuelle pour une classe est émise par un objet contenant la définition de sa principale fonction, c'est à dire le premier non-fonction virtuelle pure qui n'est pas en ligne au moment de la définition de la classe. S'il n'y a pas de fonction clé, elle est émise partout utilisée. La table virtuelle émise comprend le groupe complet de la table virtuelle pour la classe, toute nouvelle construction de tables virtuelles nécessaires pour les sous-objets, et le VTT pour la classe. Ils sont émis dans un groupe COMDAT, avec le nom de la table virtuelle mutilée comme symbole d'identification. Note que si la fonction clé n'est pas déclarée en ligne dans la définition de classe, mais que sa définition ultérieure est toujours déclarée en ligne, elle sera émise dans chaque objet contenant la définition.
NOTE : dans l'abstrait, un destructeur virtuel pur pourrait être utilisé comme fonction clé, car il doit être défini même s'il est pur. Toutefois, le Comité de L'ABI ne s'est rendu compte de ce fait qu'après l'achèvement de la spécification de la fonction clé; par conséquent, un destructeur virtuel pur ne peut pas être la fonction clé .
la deuxième section est La réponse à votre question. Un destructeur virtuel pur n'est pas une fonction clé. Par conséquent, il n'est pas clair où placer le vtable et il est placé partout. En conséquence nous obtenons le message d'avertissement.
vous trouverez même cette explication dans la Clang source documentation .
So spécifiquement à l'avertissement: Vous recevrez l'avertissement lors de toutes vos fonctions virtuelles appartenant à l'une des catégories suivantes:
-
inline
est spécifié pourA::x()
dans la définition de la classe.struct A { inline virtual void x(); virtual ~A() { } }; void A::x() { }
-
B::x() est incluse dans la définition de la classe.
struct B { virtual void x() { } virtual ~B() { } };
-
C::x() est virtuelle pure
struct C { virtual void x() = 0; virtual ~C() { } };
-
(appartient à 3.) Vous avez un destructeur virtuel pur
struct D { virtual ~D() = 0; }; D::~D() { }
dans ce cas, l'ordre pourrait être défini, car le destructeur doit être défini, néanmoins, par définition, il n'y a toujours pas de" première " unité de traduction.
pour tous les autres cas, la fonction clé est la première fonction virtuelle qui ne correspond pas à l'une de ces catégories, et le vtable sera placé dans l'Unité de traduction où la fonction clé est définie.
pour un instant, oublions les fonctions virtuelles pures et essayons de comprendre comment le compilateur peut éviter d'émettre le vtable dans toutes les unités de traduction qui incluent la déclaration d'une classe polymorphique.
quand le compilateur voit la déclaration d'une classe avec des fonctions virtuelles, il vérifie s'il y a des fonctions virtuelles qui sont seulement déclarées mais non définies dans la déclaration de classe. Si il y a exactement une telle fonction, le compilateur sait pour assurez-vous qu'il doit être défini quelque part (sinon le programme ne liera pas), et n'émet le vtable que dans l'Unité de traduction hébergeant la définition de cette fonction. S'il y a plusieurs de ces fonctions, le compilateur choisit l'une d'elles en utilisant des critères de sélection déterministes et - en ce qui concerne la décision d'émettre le vtable - ignore les autres. La manière la plus simple de sélectionner une telle fonction virtuelle représentative unique est de prendre la première de la liste des candidats, et c'est ce que fait clang.
ainsi, la clé de cette optimisation est de sélectionner une méthode virtuelle telle que le compilateur peut garantir qu'il rencontrera une (simple) définition de cette méthode dans une unité de traduction.
maintenant, que faire si la déclaration de classe contient des fonctions virtuelles pures? Un programmeur peut fournir une implémentation pour une fonction virtuelle pure mais (s)il n'est pas obligé à ! Par conséquent, les fonctions virtuelles pures n'appartiennent pas à la liste des méthodes virtuelles candidates à partir desquelles le compilateur peut choisir la méthode représentative.
mais il y a une exception - un destructeur virtuel pur!
un destructeur virtuel pur est un cas spécial:
- Une classe abstraite n'est pas logique si vous n'allez pas tirer d'autres classes.
- Une sous-classe' destructeur appelle toujours la classe de base destructeur.
- Le destructeur d'une classe dérivant d'une classe avec un destructeur virtuel est automatiquement une fonction virtuelle.
- toutes les fonctions virtuelles de toutes les classes, dont le programme crée des objets, sont généralement liées dans l'exécutable final (y compris les fonctions virtuelles qui peuvent être statiquement prouvées pour rester inutilisées, bien que cela nécessiterait une analyse statique de la programme complet).
- par conséquent, un destructeur virtuel pur doit avoir une définition fournie par l'utilisateur.
ainsi, l'avertissement de clang dans l'exemple de la question n'est pas justifié conceptuellement.
Cependant, du point de vue pratique, l'importance de cet exemple est minime, car un destructeur virtuel pur est rarement, si jamais, nécessaire. Je ne peux pas imaginer un cas plus ou moins réaliste où un pur le destructeur virtuel ne sera pas accompagné d'une autre fonction purement virtuelle. Mais dans une telle configuration le besoin de la pureté du destructeur (virtuel) disparaît complètement, puisque la classe devient abstraite en raison de la présence d'autres méthodes virtuelles pures.
j'ai fini par implémenter un destructeur virtuel trivial, plutôt que de le laisser purement virtuel.
donc au lieu de
class A {
public:
virtual ~A() = 0;
};
j'utilise
class A {
public:
virtual ~A();
};
puis mettre en œuvre le destructeur trivial dans un .fichier cpp:
A::~A()
{}
cette broche effectivement le vtable à la .le fichier cpp, au lieu de le diffuser dans plusieurs unités de traduction (objets), et évite avec succès le-Wweak-vtables avertissement.