10
votes

La mécanique de l'extension via des fonctions libres ou des fonctions des membres

Charges des bibliothèques C ++, la norme incluse, vous permet d'adapter vos objets à utiliser dans les bibliothèques. Le choix est souvent entre une fonction membre ou une fonction libre dans la même espace de noms.

J'aimerais connaître la mécanique et construire le code de la bibliothèque utilise pour envoyer un appel qui appellera l'une de ces fonctions "extension", je sais que cette décision doit avoir lieu pendant la compilation et implique des modèles. Le piégeocode d'exécution suivant n'est pas possible / non sens, les raisons sont hors de la portée de cette question. xxx

Le code ci-dessus ressemble à un code d'exécution: /. Ainsi, comment la bibliothèque découvre-t-il l'espace de noms d'une classe, comment détecte-t-il les trois conditions, quels autres pièges sont là qui doivent être évités?

La motivation de ma question est pour moi de Soyez capable de trouver les blocs d'expédition dans les bibliothèques et de pouvoir utiliser les constructions de mon propre code. Ainsi, des réponses détaillées aideront.

!! Pour gagner une prime !!

OK, donc selon la réponse de Steve (et des commentaires) ADL et Sfinae sont les constructions clés pour le câblage de l'envoi lors de la compilation. J'ai mon croisement de tête ADL (primitivement) et Sfinae (à nouveau rudement). Mais je ne sais pas comment ils deviennent ensemble dans la façon dont je pense qu'ils devraient.

Je veux voir un exemple illustratif de la manière dont ces deux constructions peuvent être réunies pour qu'une bibliothèque puisse choisir à la compilation Qu'il s'agisse d'appeler une fonction membre fournie par l'utilisateur dans un objet ou une fonction libre fournie par l'utilisateur fourni dans l'espace de nom de même objet. Cela ne devrait être effectué qu'à l'aide des deux constructions ci-dessus, aucune expédition d'exécution de tout type.

permet de dire que l'objet en question s'appelle ns :: voiture , et cet objet doit Fournissez le comportement des MoveForward (par unités int) , en tant que fonction de membre de la pièce. Si le comportement doit être ramassé à partir de l'espace de noms de l'objet, il ressemblera probablement à MoveForward (const voiture & car_, par unités int) . Permet de définir la fonction qui veut envoyer mover (NS :: Direction D, const NS :: Véhicule & V _) , où la direction est une énumération, et v_ est une classe de base de NS: : voiture .


2 commentaires

Vous ne pouvez pas remplacer opérateur <<< / code> dans votre classe pour sortir dans un flux. Un opérateur membre doit avoir la classe à gauche de l'opérateur, pas à droite. En outre, la recherche de nom fait partie du compilateur, non de la bibliothèque et impliquera des modèles uniquement s'il y a une classe de modèle ou une fonction de modèle impliquée. Si vous demandez comment la recherche de nom est faite, veuillez clarifier votre question. Sinon, je ne sais pas ce que vous demandez.


@David Oui, j'avais tort de Opérateur <<< / Code> J'ai supprimé l'exemple, mais quelqu'un édit ma question et écrasé la suppression: D


5 Réponses :


8
votes

La bibliothèque ne fait rien au moment de l'exécution, l'expédition est effectuée par le compilateur lorsque le code d'appel est compilé. Les fonctions libres dans le même espace de noms que l'un des arguments figurent selon les règles d'un mécanisme appelé "Lookup-dépendant de l'argument" (ADL), parfois appelé "Koenig Recherche".

Dans les cas où vous avez la possibilité d'implémenter une fonction libre ou une fonction de membre, il se peut que la bibliothèque fournit un modèle pour une fonction libre qui appelle la fonction Membre. Ensuite, si votre objet fournit une fonction du même nom par ADL, ce sera une meilleure correspondance que l'instanciation du modèle et sera donc choisi d'abord. Comme indique Space_C0WB0Y, ils peuvent utiliser SFIAE pour détecter la fonction de membre dans le gabarit et faire quelque chose de différent selon que cela existe ou non.

Vous ne pouvez pas changer le comportement de std :: COUT << x; En ajoutant une fonction de membre à x , donc je ne sais pas ce que tu veux signifie là-bas.


2 commentaires

L'autre mécanisme important pour la répartition est Sfinae .


Merci pour les pointeurs, cela devrait m'aider à comprendre les choses. En outre, vous aviez raison sur la sémantique du flux et j'ai supprimé l'exemple. Le dernier code que j'ai examiné a été boost :: sérialisement, qui vous permet de fournir les deux types de fonctions.



2
votes

Eh bien, je peux vous dire comment détecter la présence de fonctions membres d'un certain nom (et de signature) à la compilation. Un de mes amis le décrit ici:

Détection de l'existence des fonctions des membres Au moment de la compilation p>

Cependant, cela ne vous mènera pas où vous voulez aller, car cela ne fonctionne que pour le type statique. Puisque vous voulez transmettre un "référence-to-véhicule", il n'ya aucun moyen de tester si le type dynamique (le type de l'objet de béton derrière la référence) a une telle fonction de membre. P>

si Vous vous contentez du type statique cependant, il existe une autre façon de faire une chose très similaire. Il implémente "si l'utilisateur fournit une fonction libre surchargée, appelez-la, sinon, essayez d'appeler la fonction membre" em>. Et cela va comme ça: p> xxx pré>

de cette façon, l'utilisateur peut fournir sa propre surcharge de "the_opération", Dans la même espace de noms que sa classe, il est donc trouvé par ADL. Bien sûr "The_Operation" de l'utilisateur doit être "plus spécialisé" que votre valeur par défaut Mise en œuvre - Sinon, l'appel serait ambigu. En pratique ce n'est pas un problème cependant, puisque tout ce qui restreint le type du paramètre plus qu'il ne s'agit d'une référence-to-const à tout ce que em> sera "Plus spécialisé". p>

Exemple: P>

namespace users_ns {

class foo {};

void the_operation(foo const& f)
{
    std::cout << "foo\n";
}

template <class T>
class bar {};

template <class T>
void the_operation(bar<T> const& b)
{
    std::cout << "bar\n";
}

} // namespace users_ns


2 commentaires

Plus de mots et démonstration explicite du concept en code. +1 pour ça


Merci Mate, j'étais effectivement intéressé par le type de type statique. Voir le code écrit obtient l'effet "AHA": D



0
votes

Althument, parfois, les développeurs peuvent utiliser des fonctions libres ou des fonctions de classe, de manière interchangeable, il existe certaines situations, à utiliser l'une autre.

(1) Les fonctions d'objet / de classe ("méthodes) sont préférées lorsque la majeure partie de son purpouse affectez uniquement l'objet ou les objets sont intégrés à composer d'autres objets. P>

class MyStringClass {
  private:
    // ...
  protected:
    // ...
  // not a method, but declared, to allow access
  friend:
    bool AreEqual(MyStringClass A, MyStringClass B);
}

bool AreEqual(MyStringClass A, MyStringClass B) { ... }


0 commentaires

1
votes

Si vous recherchez simplement un exemple concret, tenez compte des éléments suivants: xxx

hasmoveforwardmember (2) est une métafonction Cela vérifie l'existence d'une fonction de membre de ce nom avec la signature Void (V :: *) (int) dans une classe donnée v . bougerforwarddispatcher (3) utilise ces informations pour appeler la fonction membre s'il existe ou redevient à appeler une fonction gratuite si elle ne le fait pas. déménageur déléguette simplement l'invocation de MoveForward à MoveForwardDisPatcher (5)

.

Comme posté, invoquera voiture :: mingleforward (1) , mais si cette fonction de membre est supprimée, renommée ou que sa signature a changé, NS :: Modifier sera appelé à la place.

Notez également que parce que déménageur est un modèle, une vérification SFINAE doit être mise en place pour conserver la sémantique de la seule autorisation d'autoriser les objets dérivés de NS :: Véhicule Pour être transmis pour V _ (4) . Pour démontrer, si on commencent (7) et décors (8) , déménageur sera appelé avec un objet de type non -hiclewithmoveforward (6) , que nous voulons interdire malgré le fait que hasmoveforwardmember :: valeur == vrai .

( NOTE : Si votre bibliothèque standard ne vient pas avec std :: activation_if et std :: is_base_of , utilisez le std :: tr1 :: ou boost :: variantes plutôt que disponible.)

La manière dont ce type de code est généralement utilisé consiste à appeler la fonction libre et à mettre en œuvre le fonction libre en termes de quelque chose comme bougerforwarddispatcher de telle sorte que la fonction libre appelle simplement la fonction de membre de l'objet transgé si elle existe, sans avoir à écrire des surcharges de cette fonction gratuite pour tout type possible que possible Fonction de membre.


0 commentaires

0
votes

Si j'ai bien compris votre problème, votre problème est simplement résolu en utilisant (peut-être plusieurs) héritage. Vous avez quelque part une fonction d'espace de noms d'espace de noms: xxx

Utilisez une classe de base qui transmet la même fonction: xxx

si une classe A dérive de Quelquebase n'en mettant pas implémenter dossier () appelant tandon appellera quelque chosebase :: dossomething () -> ns :: dossomething "(): xxx

si une autre classe B dérivant de quelque chose debase () appelera que cela appellera b :: dossomething (): xxx

alors appelant dossition () sur un objet dérivé de quelque chose exécutera le membre si existant ou la fonction libre autrement. Notez qu'il n'y a rien à jeter, vous obtenez une erreur de compilation s'il n'y a pas de correspondance à votre appel. xxx


0 commentaires