50 votes

Réduire la duplication de code tout en définissant une opération commutative

J'ai un un ensemble de surcharges d'une commutative fonction binaire nommée overlap, qui accepte deux types distincts:

class A a; class B b;
bool overlap(A, B);
bool overlap(B, A);

Ma fonction overlap retourne true si et seulement si une forme chevauche l'autre - c'est un exemple courant utilisé lors de la discussion multimethods.

Parce qu' overlap(a, b) est équivalent à overlap(b, a), j'ai seulement besoin de mettre en œuvre un "côté" de la relation. Un répétitives solution est d'é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ère ne pas écrire un extra - N! / 2 trivial versions de la même fonction en leur permettant d'être généré à la place, à l'aide d'un modèle.

template <typename T, typename U> 
bool overlap(T&& t, U&& u) 
{ return overlap(std::forward<U>(u), std::forward<T>(t)); }

Unfortuately, c'est enclin à répéter à l'infini, ce qui n'est pas acceptable: voir http://coliru.stacked-crooked.com/a/20851835593bd557

Comment puis-je empêcher que de tels récursion infinie? Suis-je en approchant le problème correctement?

64voto

Quentin Points 3904

Voici une solution simple:

 template <typename T, typename U> 
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écursion car il s'agit d'une correspondance exacte (veillez à 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>(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 :)

12voto

Deduplicator Points 9096

Comme un homme sage l’a déjà dit, il n’ya pas de problème que vous ne pouvez pas résoudre avec une couche supplémentaire d’indirection, sauf trop de couches d’indirection.

Donc, utilisez SFINAE et un peu d’indirection pour le faire:

 template<class A, class B>
auto overlap(A&& a, B&& b)
-> decltype(overlap_impl('\0', std::forward<A>(a), std::forward<B>(b)))
{ return overlap_impl('\0', std::forward<A>(a), std::forward<B>(b)); }

template<class A, class B>
auto overlap_impl(int, A&& a, B&& b)
-> decltype(do_overlap(std::forward<A>(a), std::forward<B>(b)))
{ return do_overlap(std::forward<A>(a), std::forward<B>(b)); }

template<class A, class B>
auto overlap_impl(long, B&& b, A&& a)
-> decltype(do_overlap(std::forward<A>(a), std::forward<B>(b)))
{ return do_overlap(std::forward<A>(a), std::forward<B>(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.
 

1voto

K. Kirsz Points 1002

Vous pouvez renommer la méthode actuelle en overlap_impl et appeler celle-ci dans le modèle. Je vais casser la récursion:

 bool overlap_impl(A a, B b) { /* check for overlap */ }

template <typename T, typename U> 
bool overlap(T&& t, U&& u) 
{ return overlap_impl(std::forward<U>(u), std::forward<T>(t)); }

template<> bool overlap(A&& t, B&& u)
{ return overlap_impl(std::forward<A>(t), std::forward<B>(u)); }
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X