Le problème est que les deux function<int()>
et function<int(int)>
sont constructibles à partir de la même fonction. C'est ce que le constructeur déclaration de std::function
ressemble dans VS2010:
template<class _Fx>
function(_Fx _Func, typename _Not_integral<!_Is_integral<_Fx>::value, int>::_Type = 0);
Ignorant la SFINAE partie, il est constructible à partir de pratiquement n'importe quoi.
std::/boost::function
utilisent une technique appelée type d'effacement, afin de permettre arbitraire des objets/fonctions passées, tant qu'ils satisfont à la signature lors de l'appelé. Un inconvénient de cette est que vous obtenez un message d'erreur dans la partie la plus profonde de la mise en œuvre (où l'sauvé la fonction est appelée), lors de la fourniture d'un objet qui ne peut pas être appelé comme la signature veut, au lieu de dans le constructeur.
Le problème peut être illustré par ce peu de classe:
template<class Signature>
class myfunc{
public:
template<class Func>
myfunc(Func a_func){
// ...
}
};
Maintenant, lorsque le compilateur recherche valide les fonctions de la surcharge de jeu, il essaie de convertir les arguments si pas parfait ajustement de la fonction existe. La conversion peut se faire par le constructeur de la paramètre de la fonction, ou par l'intermédiaire d'un opérateur de conversion de l'argument donné à la fonction. Dans notre cas, c'est l'ancien.
Le compilateur essaie de la première surcharge de a
. Pour le rendre viable, il doit faire une conversion. Pour convertir un int(*)()
d'un myfunc<int()>
, il tente le constructeur de myfunc
. Être un modèle qui prend n'importe quoi, la conversion naturellement réussit.
Maintenant, il tente la même chose avec le deuxième surcharge. Le constructeur étant toujours le même et toujours rien donné, la conversion fonctionne aussi.
Être de gauche avec 2 fonctions dans la surcharge de jeu, le compilateur est un panda triste et ne sait pas quoi faire, alors il a simplement dit que l'appel est ambiguë.
Donc en fin de compte, l' Signature
partie du modèle appartient au type lors de la prise de déclarations et les définitions, mais n'est pas quand vous voulez pour construire un objet.
Edit:
Avec toute mon attention sur le répondeur le titre de la question, j'ai totalement oublié votre deuxième question. :(
Puis-je contourner ou vais-je garder l' (ennuyeux) conversions explicites?
Autant que je sache, vous avez 3 options.
- Garder le casting
-
Faire un function
objet du type approprié et de l'
function<int()> fx = x;
function<int(int)> fy = y;
a(fx);
a(fy);
Masquer le long de la coulée dans une fonction et l'utilisation TMP pour obtenir le droit de signature
La TMP (modèle de la métaprogrammation) version est assez verbeux et de code réutilisable, mais il se cache le casting de la part du client. Un exemple peut être trouvé ici, qui repose sur l' get_signature
metafunction qui est spécialisée partielle sur la fonction des types pointeur (et fournit un bon exemple de la manière dont la correspondance de motif peut travailler en C++):
template<class F>
struct get_signature;
template<class R>
struct get_signature<R(*)()>{
typedef R type();
};
template<class R, class A1>
struct get_signature<R(*)(A1)>{
typedef R type(A1);
};
Bien sûr, cela doit être étendu pour le nombre d'arguments que vous voulez soutenir, mais c'est fait une fois et puis enterré dans un "get_signature.h"
- tête. :)
Une autre option que je considère mais immédiatement jetés était SFINAE, qui serait d'introduire encore plus de code réutilisable, que le TMP version.
Donc, oui, que sont les options que je connais. Espérons que l'un d'entre eux travaille pour vous. :)