J’ai un ensemble de surcharges d’une fonction binary commutative nommée overlap
, qui accepte deux types distincts:
class A a; class B b; bool overlap(A, B); bool overlap(B, A);
Mon overlap
fonctions renvoie true si et seulement si une forme chevauche l’autre – c’est un exemple courant utilisé lorsqu’on discute de méthodes multiples .
Étant donné que le overlap(a, b)
est équivalent à un overlap(b, a)
, il me suffit de mettre en œuvre un “côté” de la relation. Une solution répétitive consiste à écrire quelque chose comme ceci:
bool overlap(A a, B b) { /* check for overlap */ } bool overlap(B b, A a) { return overlap(a, b); }
Mais je préférerais ne pas écrire un N! / 2
supplémentaire N! / 2
N! / 2
versions sortingviales de la même fonction en leur permettant d’être générées à la place, en utilisant un modèle.
template bool overlap(T&& t, U&& u) { return overlap(std::forward(u), std::forward(t)); }
Malheureusement, cela a tendance à se déclencher à l’infini, ce qui n’est pas acceptable: voir http://coliru.stacked-crooked.com/a/20851835593bd557
Comment puis-je empêcher une telle récursion infinie? Est-ce que j’aborde le problème correctement?
Voici une solution simple:
template void overlap(T t, U u) { void overlap(U, T); overlap(u, t); }
Le modèle lui-même déclare la fonction cible, qui sera préférée à la récursivité, car elle correspond exactement (assurez-vous de prendre en compte la constance et la référence dans votre cas réel). Si la fonction n’a pas été implémentée, vous obtenez une erreur de l’éditeur de liens:
/tmp/cc7zinK8.o: In function `void overlap(C, D)': main.cpp:(.text._Z7overlapI1C1DEvT_T0_[_Z7overlapI1C1DEvT_T0_]+0x20): undefined reference to `overlap(D, C)' collect2: error: ld returned 1 exit status
… qui pointe directement sur la fonction manquante 🙂
Comme l’a dit un homme avisé, il n’y a pas de problème que vous ne puissiez résoudre avec une couche supplémentaire d’indirection, à l’exception d’un trop grand nombre de couches d’indirection.
Donc, utilisez SFINAE et une certaine indirection pour le faire:
template auto overlap(A&& a, B&& b) -> decltype(overlap_impl('\0', std::forward(a), std::forward(b))) { return overlap_impl('\0', std::forward(a), std::forward(b)); } template auto overlap_impl(int, A&& a, B&& b) -> decltype(do_overlap(std::forward(a), std::forward(b))) { return do_overlap(std::forward(a), std::forward(b)); } template auto overlap_impl(long, B&& b, A&& a) -> decltype(do_overlap(std::forward(a), std::forward(b))) { return do_overlap(std::forward(a), std::forward(b)); } // You can provide more choices if you want, for example to use member-functions. // Implement `do_overlap(A, B)`, maybe with references, in at least one direction.
Vous pouvez renommer la méthode actuelle en quelque chose comme overlap_impl
et appeler celui-ci dans le modèle. Je vais rompre la récursivité:
bool overlap_impl(A a, B b) { /* check for overlap */ } template bool overlap(T&& t, U&& u) { return overlap_impl(std::forward(u), std::forward(t)); } template<> bool overlap(A&& t, B&& u) { return overlap_impl(std::forward(t), std::forward(u)); }