Bon comme je me sens d'attaque, vla un truc fait sur le coin de la table.
Etape 1: detecter qu'un type T supporte operator*
En gros, on veut savoir si pour deux instances de T a et b, a*b est correcte.
L'idée est en gros d'essayer de caluler le type de retour de a*b, si on arrive * existe,
sinon non. Or, on veut pouvoir faire ça sans avoir d'erreur de compilation.
L'idée est d'alors d'utiliser le mécanisme de surcharge de fonction pour tomber dans un cas
foireux dans les moments ou * n'existe pas, et discriminer sur cet appel.
Le code:
Code :
- #include <iostream>
- #include <boost/mpl/bool.hpp>
- #include <boost/type_traits/remove_cv.hpp>
- using namespace std;
- namespace detail
- {
- struct tag {};
- struct any { template<class T> any(T const& ); };
- tag operator*(any const&, any const& );
- char (& check(tag))[2];
- template <class T> char check(T const& );
- template<class T>
- struct has_operator_multiplies_impl
- {
- static typename boost::remove_cv<T>::type& x;
- static const bool value = sizeof( check((x * x)) ) == 1;
- };
- }
- template <class T>
- struct has_operator_multiplies
- : boost::mpl::bool_<detail::has_operator_multiplies_impl<T>::value> {};
|
En gros, on cherche à calculer le sizeof de x*x. Soit x supporte cette operateur
et son type de retour tombe dans template <class T> char check(T const& )
soit non. Si il n'existe pas que ce passe-t-il ? et bien, c'est le * de any (qui est constructible
à partir de n'importe quoi) qui va etre selectionné. Il renvoit un tag qui va faire que l'on
va appeler char (& check(tag))[2];
Le sizeof de tout ça est donc : 1 si check renvoit char et 2 si il renvoit char[2].
On a donc discriminé la chose.
Exemple: http://codepad.org/ia244fNX
Etape 2: Utilisez ça dans une classe template
Maintenant qu'on sait que T a ou pas un operator*, il faut pouvoir utiliser cette information pour discriminer des classes.
On fait juste de la SFINAE avec enable_if.
Code :
- template<class T, class Enable = void>
- struct do_some_multiplication;
- template<class T>
- struct do_some_multiplication<T, typename boost::enable_if<has_operator_multiplies<T> >::type>
- {
- void test(T const& a, T const& b )
- {
- cout << a*b << endl;
- }
- };
|
Si le has_operator_multiplies<T> renvoit vrai pour T, la seconde specialsiation est prise. Sinon, c'ets la premiere.
Soit on ne mets rien pour obtenir une erreur du genre "incomplete type", soit on met sune static_assert.
Exemple: http://codepad.org/PIx3L7LS
Notes
les has_operator_xxx ont été plus ou moins ajouter à Boost.typetraits mais je sais pas si c'ets dans la 1.43.
La stratégie à base de sizeof ce generalise avec une mini-macro, on peut aussi demander l'existence de methode avec une signature donnée ou l'existence de fonction avec un prototype donnée etc...
Message édité par Joel F le 05-05-2010 à 16:30:19