Question

Je peux me tromper totalement ici, mais si je comprends bien, C ++ n’a pas vraiment de pointeur & natif vers la fonction membre & "; type. Je sais que vous pouvez faire des trucs avec Boost et mem_fun, etc. Mais pourquoi les concepteurs de C ++ ont-ils décidé de ne pas avoir de pointeur 64 bits contenant un pointeur sur la fonction et un pointeur sur l'objet, par exemple?

Ce que je veux dire en particulier est un pointeur sur une fonction membre d'un objet particulier de type inconnu . C'EST À DIRE. vous pouvez utiliser pour un rappel . Ce serait un type qui contient deux valeurs. La première valeur est un pointeur sur la fonction , et la deuxième valeur est un pointeur sur l'instance spécifique de l'objet.

Ce que je ne veux pas dire, c'est un pointeur sur une fonction membre générale d'une classe. E.G.

int (Fred::*)(char,float)

Cela aurait été si utile et aurait simplifié ma vie.

Hugo

Était-ce utile?

La solution

@RocketMagnet - Ceci est une réponse à votre autre question , celle qui était étiquetée comme un doublon. Je réponds à cette question et non celle-ci.

En général, le pointeur C ++ vers les fonctions membres ne peut pas être converti de manière portable dans la hiérarchie de classes. Cela dit, vous pouvez souvent vous en tirer. Par exemple:

#include <iostream>
using std::cout;
class A { public: int x; };
class B { public: int y; };
class C : public B, public A { public: void foo(){ cout << "a.x == " << x << "\n";}};

int main() {
    typedef void (A::*pmf_t)();
    C c; c.x = 42; c.y = -1;

    pmf_t mf = static_cast<pmf_t>(&C::foo);
    (c.*mf)();
}

Compilez ce code et le compilateur à juste titre se plaint:

$ cl /EHsc /Zi /nologo pmf.cpp
pmf.cpp
pmf.cpp(15) : warning C4407: cast between different pointer to member representations, compiler may generate incorrect code

$

Donc, pour répondre à la question & "Pourquoi C ++ n’a-t-il pas un pointeur sur la fonction membre sur la classe vide? &"; Est-ce que cette classe imaginaire de base de tout n'a pas de membres, il n'y a donc aucune valeur que vous pouvez lui attribuer en toute sécurité! " void (C :: ) () " et " void (void :: ) () " sont des types mutuellement incompatibles.

Maintenant, je parie que vous pensez & "attendez, j’ai très bien projeté les pointeurs sur les fonctions des membres avant!" Oui, vous pouvez avoir, en utilisant reinterpret_cast et l'héritage simple. C’est dans la même catégorie que d’autres réinterprétations:

#include <iostream>
using std::cout;
class A { public: int x; };
class B { public: int y; };
class C : public B, public A { public: void foo(){ cout << "a.x == " << x << "\n";}};
class D { public: int z; };

int main() {
    C c; c.x = 42; c.y = -1;

    // this will print -1
    D& d = reinterpret_cast<D&>(c);
    cout << "d.z == " << d.z << "\n";
}

Donc, si void (void::*)() existait, mais que vous ne pouvez rien lui attribuer en toute sécurité / de manière portable.

Traditionnellement, vous utilisez des fonctions de signature void (*)(void*) partout où vous voudriez utiliser <=>, car, bien que les pointeurs de fonction de membre ne jettent pas correctement dans la hiérarchie de l'héritage, les pointeurs vides le sont bien. Au lieu de cela:

#include <iostream>
using std::cout;
class A { public: int x; };
class B { public: int y; };
class C : public B, public A { public: void foo(){ cout << "a.x == " << x << "\n";}};

void do_foo(void* ptrToC){
    C* c = static_cast<C*>(ptrToC);
    c->foo();
}

int main() {
    typedef void (*pf_t)(void*);
    C c; c.x = 42; c.y = -1;

    pf_t f = do_foo;
    f(&c);
}

Donc à votre question. Pourquoi C ++ ne supporte-t-il pas ce type de casting? Les types de fonction pointeur vers membre doivent déjà traiter des classes de base virtuelles et non virtuelles, ainsi que des fonctions membres virtuelles et non virtuelles, toutes du même type, en les gonflant à 4 * sizeof (void *) sur certaines plates-formes. Je pense que cela compliquerait davantage la mise en œuvre de la fonction pointeur à membre, et que les pointeurs de fonction bruts résolvent déjà si bien ce problème.

Comme d'autres l'ont déjà fait remarquer, C ++ fournit aux rédacteurs de bibliothèques assez d'outils pour y parvenir, puis aux programmeurs "normaux" comme vous et moi qui devrions utiliser ces bibliothèques au lieu de transpirer ces détails.

EDIT: wiki communautaire marqué. Modifiez uniquement pour inclure les références pertinentes à la norme C ++ et ajoutez-les en italique. (en particulier, ajoutez des références à une norme où ma compréhension était fausse! ^ _ ^)

Autres conseils

Comme d'autres l'ont souligné, C ++ a un type de pointeur de fonction membre.

Le terme que vous recherchiez est & "fonction liée &". La raison pour laquelle C ++ ne fournit pas de sucre de syntaxe pour la liaison de fonctions tient à sa philosophie qui consiste à ne fournir que les outils les plus fondamentaux, avec lesquels vous pouvez ensuite créer tout ce que vous voulez. Cela aide à garder la langue & Quot; petite & Quot; (ou du moins, moins ahurissant).

De même, C ++ ne possède pas de primitive de verrouillage {} semblable à celle de C #, mais il possède un code RAII utilisé par scoped_lock de boost.

Il existe bien sûr une école de pensée qui dit que vous devriez ajouter du sucre de syntaxe à tout ce qui pourrait être utile. Pour le meilleur ou pour le pire, le C ++ n'appartient pas à cette école.

Je pense que ce que vous recherchez pourrait être dans ces bibliothèques ...

Délégués rapides http://www.codeproject.com/KB/cpp/FastDelegate .aspx

Boost.Function http: //www.boost. org / doc / libs / 1_37_0 / doc / html / function.html

Et voici une explication très complète des questions relatives au pointeur de fonction http://www.parashift.com/c++-faq-lite/pointers-to-members.html

C'est le cas.

Par exemple,

int (Fred::*)(char,float)

est un pointeur sur une fonction membre d'une classe Fred qui retourne un int et prend un char et un float.

Je pense que la réponse est que les concepteurs de C ++ ont choisi de ne pas avoir dans le langage ce qui pourrait être implémenté aussi facilement dans une bibliothèque. Votre propre description de ce que vous voulez donne un moyen parfaitement raisonnable de la mettre en œuvre.

Je sais que cela semble drôle, mais C ++ est un langage minimaliste. Ils ont laissé aux bibliothèques tout ce qu’ils pouvaient leur laisser.

Le TR1 a std :: tr1 :: function et sera ajouté à C ++ 0x. Donc, dans un sens, il l’a.

L'une des philosophies de conception de C ++ est la suivante: vous ne payez pas pour ce que vous n'utilisez pas. Le problème avec les styles C # est qu’ils sont lourds et nécessitent un support linguistique, que tout le monde paierait pour qu’ils les utilisent ou non. C’est pourquoi l’implémentation de la bibliothèque est préférable.

Si les délégués sont lourds, c’est qu’un pointeur de méthode est souvent plus grand qu’un pointeur normal. Cela se produit chaque fois que la méthode est virtuelle. Le pointeur de la méthode appellera une fonction différente selon la classe de base qui l'utilise. Cela nécessite au moins deux pointeurs, la table vtable et le décalage. La méthode comporte une autre bizarrerie qui provient d'une classe impliquée dans l'héritage multiple.

Tout cela étant dit, je ne suis pas un rédacteur de compilateur. Il aurait peut-être été possible de créer un nouveau type pour les pointeurs de méthodes liées qui renverserait la virtualité de la méthode référencée (après tout, nous savons quelle est la classe de base si la méthode est liée).

Le problème n’est sûrement pas le principe de base qui consiste à avoir un pointeur d’objet et un pointeur de fonction dans un package convivial, car vous pouvez le faire à l’aide d’un pointeur et d’un thunk. (De tels thunks sont déjà utilisés par VC ++ sur x86 pour prendre en charge les pointeurs vers les fonctions membres virtuelles, de sorte que ces pointeurs ne prennent que 4 octets.) Vous pourriez vous retrouver avec beaucoup de thunks, c’est vrai, mais les gens comptent déjà sur l’éditeur de liens pour éliminez les instanciations de modèles en double - je le fais quand même - et il n'y a que très peu de vtable et cela compensera dans la pratique. Les frais généraux ne seraient probablement pas importants pour un programme de taille raisonnable, et si vous n'utilisez pas ce logiciel, cela ne vous coûtera rien.

(Les architectures qui utilisent traditionnellement une table des matières stockent le pointeur de la table des matières dans la partie pointeur de la fonction, plutôt que dans le thunk, comme elles devraient déjà le faire.)

(Ce nouveau type d'objet ne serait pas exactement substituable à un pointeur normal à fonctionner, bien sûr, car la taille serait différente. Ils seraient toutefois écrits de la même façon au point d'appel.)

Le problème que je vois est celui de la convention d’appel: supporter les pointeurs de fonctions de cette manière pourrait être délicat dans le cas général, car le code généré devrait préparer les arguments (ceci inclus) de la même manière, sans se soucier des type réel d'objet, de fonction ou de fonction membre sur lequel pointe le pointeur.

Ce n’est probablement pas un gros problème pour x86, du moins pas avec thiscall, car vous pouvez charger ECX indépendamment de la réalité et accepter que si la fonction d’appel n’en a pas besoin, elle sera fictive. (Et je pense que VC ++ suppose que ECX est faux dans ce cas de toute façon.) Mais sur les architectures qui transmettent des arguments pour des paramètres nommés dans des registres à des fonctions, vous pouvez vous retrouver avec une quantité considérable de brassage dans le thunk, et si les arguments de pile sont poussés à gauche -à-droite alors vous êtes essentiellement bourré. Et cela ne peut pas être corrigé de manière statique, car dans la limite, il n’ya pas d’informations sur les unités de traduction croisée.

[Edit: MSalters, dans un commentaire sur le post de rocketmagnet ci-dessus, indique que si les fonctions objet ET sont connues, le décalage de cette valeur, etc., peut être déterminé immédiatement. Cela ne m'est totalement pas arrivé! Mais, gardant cela à l’esprit, je suppose qu’il suffit de stocker le pointeur exact de l’objet, éventuellement le décalage, et le pointeur exact de la fonction. Cela rend les thunks totalement inutiles - je pense - mais je suis à peu près sûr que la question de l'indication des fonctions membres et des fonctions non membres resterait la même.]

C ++ est déjà un gros langage, et ajouter ceci l’aurait rendu plus gros. Ce que vous voulez vraiment est pire qu’une simple fonction membre liée, c’est quelque chose de plus proche de boost :: function. Vous souhaitez stocker un void(*)() et une paire pour les rappels. Après tout, la raison en est que vous souhaitez donner à l'appelant un rappel complet et l'appelant ne doit pas se soucier des détails exacts.

La taille serait probablement sizeof(void*)+sizeof(void(*)()). Les pointeurs vers les fonctions membres peuvent être plus grands, mais c'est parce qu'ils ne sont pas liés. Ils doivent par exemple gérer la possibilité que vous preniez l'adresse d'une fonction virtuelle. Cependant, un type intégré de fonction pointeur-lié-à-membre ne souffrirait pas de cette surcharge. Il peut résoudre la fonction exacte à appeler au moment de la liaison.

Cela n’est pas possible avec un UDT. boost :: function ne peut pas ignorer la surcharge d'un fichier PTMF lorsqu'il lie le pointeur d'objet. Vous devez savoir comprendre la structure d'un fichier PTMF, vtable, etc. - tous les éléments non standard. Cependant, nous pourrions y arriver maintenant avec C ++ 1x. Une fois que c'est dans std ::, c'est un jeu juste pour les vendeurs de compilateurs. L’implémentation de la bibliothèque standard elle-même n’est pas portable (voir, par exemple, type_info).

Je suppose que vous voudriez quand même avoir une belle syntaxe, même si un fournisseur de compilateur l'implémente dans la bibliothèque. Je voudrais std::function<void(*)()> foo = &myX && X::bar. (Cela n'entre pas en conflit avec la syntaxe existante, car X :: bar n'est pas une expression - seulement & Et X :: bar est)

Il me semble que les méthodes ont un argument this implicite. Par conséquent, un pointeur c sur une méthode est insuffisant pour permettre à la méthode d'être appelée (car il n'existe aucun moyen de déterminer quelle instance doit être utilisée pour < => (ou même si une quelconque instance est actuellement existante)).

Modifier: Rocketmagnet a déclaré avoir abordé la question dans la question, ce qui semble bien être le cas, même si je pense que cela a été ajouté après le début de cette réponse. mais je vais dire & "; mea culpa &" ;, de toute façon.

Permettez-moi donc de développer un peu la pensée.

C ++ est étroitement lié à c et a tous ses types intrinsèques compatibles avec le langage antérieur (principalement en raison de l’histoire de c++ développement, je suppose). Ainsi, un pointeur <=> intrinsèque est un pointeur <=> et est incapable de supporter l'utilisation que vous demandez.

Certes, vous pouvez créer un type dérivé pour faire le travail (comme dans l'implémentation boost), mais une telle créature appartient à une bibliothèque.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top