Pourquoi cet appel de fonction surchargée est-il ambigu?

Pourquoi cet appel de constructeur est-il ambigu?

#include  class A { std::function f_; std::function g_; public: A(std::function f) { f_ = f; } A(std::function g) { g_ = g; } ~A() {} }; int main() { A a([](){ return (int)1; }); return 0; } 

Notez le transtypage.

Existe-t-il un moyen d’indiquer au compilateur quelle surcharge de constructeur utiliser?

C’est un défaut de la norme. Voir DR 2132 :

Considérer ce qui suit:

 #include  void f(std::function) {} void f(std::function) {} int main() { f([]{}); f([](int){}); } 

Les appels à f in main sont ambigus. Apparemment parce que les séquences de conversion en std::function des lambdas sont identiques. La norme spécifie que l’object fonction donné à std::function “doit être appelable (20.8.11.2) pour les types d’argument ArgTypes et le type de retour R ” Cela ne dit pas que si ce n’est pas le cas, le constructeur ne fait pas partie du jeu de surcharge.

Essayez d’utiliser un pointeur de fonction comme argument à la place:

 A(int f()) { f_ = f; } A(float g()) { g_ = g; } 

Parce que ce que vous transmettez ne correspond pas aux types, nous entrons dans des séquences de conversion pour trouver la surcharge à utiliser. Les deux versions de function peuvent être créées implicitement à partir d’un object lambda qui renvoie int. Ainsi, le compilateur ne peut pas choisir lequel créer. bien que cela semble intuitivement évident, les règles en C ++ ne le permettent pas.

Modifier:

Ecrit au pied levé mais je pense que cela pourrait faire l’affaire:

 template < typename Fun > typename std::enable_if::type, int>::value>::type f(Fun f) ... template < typename Fun > typename std::enable_if::type, double>::value>::type f(Fun f) ... 

etc … Ou vous pouvez utiliser le dispatching de tags:

 template < typename Fun, typename Tag > struct caller; template < typename T > tag {}; template < typename Fun > struct caller> { static void call(Fun f) { f(); } }; // etc... template < typename Fun > void f(Fun fun) { caller>::call(fun); } 
 template, std::enable_if_t{}&&(!std::is_convertible{}||std::is_integral{}),int> =0 > A(F&&f):A(std::function(std::forward(f))){} template, std::enable_if_t{}&&(!std::is_convertible{}||std::is_floating_point{}, int> =0 > A(F&&f):A(std::function(std::forward(f))){} A(std::function f) { f_ = f; } A(std::function g) { g_ = g; } 

Ici, nous prenons les cas ambigus, et envoyons les cas intégraux à la surcharge int et non flottants à celui qui flotte.

Les cas où il est convertible aux deux, mais ni virgule flottante ni intégrale, restnt ambigus. Comme ils le devraient.

La clause sfina peut être délicate. Si cela ne fonctionne pas, remplacez:

  std::enable_if_t{}&&(!std::is_convertible{}||std::is_floating_point{}, int> =0 

Avec

  class=std::enable_if_t{}&&(!std::is_convertible{}||std::is_floating_point{}> 

et semblable pour l’autre ctor. Cela pourrait fonctionner dans MSVC par exemple.

L’envoi de balises complet peut être requirejs pour msvc, en raison du manque d’expression sfinae en 2015.