Pages

samedi 16 mai 2015

Délégation d'appel de fonctions utilitaires.

Cet article fait suite à l'utilisation de swap et des fonctions utilitaires en général.

L'article précédemment cité montre pourquoi il faut exporter avec using la fonction utilitaire généraliste avant de l'utiliser. La nécessité de "déplacer" les fonctions dans le scope est lourd et facile à oublier.

L'idéal serait la présence d'une unique fonction en charge d'appeler la bonne surcharge. Heureusement, une solution existe. Elle consiste en la création d'un namespace dans lequel la fonction générale est exportée et où une fonction intermédiaire l'appelle. Comme la règle d'adl s'applique, la fonction surchargée sera appelée si existante.

#include <algorithm>

namespace fn {
  namespace {
    using std::swap;

    template<class T>
    void swap_impl(T & a, T & b)
    { swap(a,b); }
  }

  template<class T>
  void swap(T & x, T & y)
  { ::fn::swap_impl(x,y); }
}

Cependant, cette solution souffre de l'effet inverse: la fonction ne peut pas être déplacée dans le même scope qu'une fonction généraliste. Il y aurait 2 prototypes identiques, le compilateur ne pourrait pas lever l’ambiguïté. Comme il est très courant de voir `using namespace std;`, un `using fn::swap` dans le même scope rentrerait en conflit avec std::swap lors de l'appel (aucun problème si dans un sous-scope).

#include <iostream>

namespace my {
  struct A{};
  void swap(A&, A&)
  { std::cout << "ok\n"; }
}

int main()
{
  {
    my::A a, b;
    fn::swap(a,b); // affiche ok
  }
  {
    int a, b;
    fn::swap(a,b); // compile
  }

  {
    using fn::swap;
    using std::swap;
    int a, b;
    swap(a,b); // ambiguïté
  }

  {
    using std::swap;
    {
      using fn::swap;
      my::A a, b;
      fn::swap(a,b); // affiche ok
    }
  }
}

Aucun commentaire: