C ++ 11 ne déduit pas le type lorsque les fonctions std :: function ou lambda sont impliquées

Quand je définis cette fonction,

template set test(const set& input) { return input; } 

Je peux l’appeler en utilisant test(mySet) ailleurs dans le code sans avoir à définir explicitement le type de modèle. Cependant, lorsque j’utilise la fonction suivante:

 template set filter(const set& input,function compare) { set ret; for(auto it = input.begin(); it != input.end(); it++) { if(compare(*it)) { ret.insert(*it); } } return ret; } 

Lorsque j’appelle cette fonction en utilisant filter(mySet,[](int i) { return i%2==0; }); Je reçois l’erreur suivante:

erreur: pas de fonction correspondante pour l’appel à ‘filter (std :: set &, main (): :)’

Cependant, toutes ces versions fonctionnent:

 std::function func = [](int i) { return i%2 ==0; }; set myNewSet = filter(mySet,func); set myNewSet = filter(mySet,[](int i) { return i%2==0; }); set myNewSet = filter(mySet,function([](int i){return i%2==0;})); 

Pourquoi c ++ 11 est-il incapable de deviner le type de modèle lorsque je mets la fonction lambda directement dans l’expression sans créer directement une std::function ?

MODIFIER:

D’après l’avis de Luc Danton dans les commentaires, voici une alternative à la fonction que j’avais précédemment et qui ne nécessitait pas que les modèles soient transmis explicitement.

 template set filter(const set& input,CompareFunction compare) { set ret; for(auto it = input.begin(); it != input.end(); it++) { if(compare(*it)) { ret.insert(*it); } } return ret; } 

Ceci peut être appelé par set result = filter(myIntSet,[](int i) { i % 2 == 0; }); sans avoir besoin du modèle.

Le compilateur peut même deviner les types de retour dans une certaine mesure, en utilisant le nouveau mot-clé decltype et en utilisant la nouvelle syntaxe du type de retour de fonction. Voici un exemple qui convertit un ensemble en une carte, en utilisant une fonction de filtrage et une fonction qui génère les clés en fonction des valeurs:

 template auto filter(const set& input,CompareType compare,IndexType index) -> map { map ret; for(auto it = input.begin(); it != input.end(); it++) { if(compare(*it)) { ret[index(*it)] = *it; } } return ret; } 

Il peut également être appelé sans utiliser le modèle directement, comme

 map s = filter(myIntSet,[](int i) { return i%2==0; },[](int i) { return toSsortingng(i); }); 

La question concerne la nature des lambdas. Ce sont des objects de fonction avec un ensemble de propriétés fixe selon la norme, mais ils ne sont pas une fonction. Le standard détermine que lambdas peut être converti en std::function<> avec les types exacts d’arguments et, s’ils n’ont pas d’état, des pointeurs de fonctions.

Mais cela ne signifie pas qu’un lambda est une std::function ni un pointeur de fonction. Ce sont des types uniques implémentant operator() .

La déduction de type, par contre, ne fera que déduire des types exacts, sans conversions (autres que les qualifications const / volatiles). Parce que le lambda n’est pas une std::function le compilateur ne peut pas déduire le type dans l’appel: filter(mySet,[](int i) { return i%2==0; }); être une std::function<> .

Comme pour les autres exemples, dans le premier, vous convertissez le lambda en type de fonction, puis transmettez-le. Le compilateur peut en déduire le type, comme dans le troisième exemple où la std::function est une valeur (temporaire) du même type.

Si vous fournissez le type d’instanciation int au modèle, deuxième exemple de travail, la déduction n’entre pas en jeu, le compilateur utilisera le type, puis convertira le lambda en le type approprié.

Oubliez votre cas. car c’est trop complexe pour l’parsing.

Prenons cet exemple simple:

  template struct X { X(T data) {} }; template void f(X x) {} 

Appelez maintenant f comme:

  f(10); 

Ici, vous pourriez être tenté de penser que T sera déduit à int et, par conséquent, l’appel de fonction ci-dessus devrait fonctionner. Eh bien, ce n’est pas le cas. Pour garder les choses simples, imaginez qu’il existe un autre constructeur qui prend int comme:

  template struct X { X(T data) {} X(int data) {} //another constructor }; 

Maintenant, à quoi T devrait-on déduire, quand j’écris f(10) ? Eh bien, T pourrait n’importe quel type.

Notez qu’il pourrait y avoir beaucoup d’autres cas similaires. Prenez cette spécialisation, par exemple:

  template struct X //specialized for pointers { X(int data) {}; }; 

Maintenant, à quoi T devrait-on déduire pour l’appel f(10) ? Maintenant, ça semble encore plus difficile.

C’est donc un contexte non déductible, ce qui explique pourquoi votre code ne fonctionne pas pour std::function qui est un cas identique. Notez que les lambdas ne sont pas de type std::function – ce sont essentiellement des instances de classes générées par le compilateur (c’est-à-dire des foncteurs de types différents de std::function ).