2014-09-17 4 views
11

explicitement tapé j'ai ce code:ne peuvent pas utiliser lambda

std::function<std::string&(std::string&)> change_str = [](std::string& str){ 
    return (str = "Hello world!"); 
}; 

std::string s; 

std::cout << change_str(s) << std::endl; 

Il ne compile pas, et dire:

main.cpp:8:47: error: no viable conversion from '(lambda at main.cpp:8:60)' to 'std::function<std::string &(std::string &)>' 
    std::function<std::string&(std::string&)> change_str = [](std::string& str){ 
              ^   ~~~~~~~~~~~~~~~~~~~~~ 
/usr/include/c++/v1/functional:1448:5: note: candidate constructor not viable: no known conversion from '(lambda at main.cpp:8:60)' to 'nullptr_t' for 1st argument 
    function(nullptr_t) _NOEXCEPT : __f_(0) {} 
    ^
/usr/include/c++/v1/functional:1449:5: note: candidate constructor not viable: no known conversion from '(lambda at main.cpp:8:60)' to 'const std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > &(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > &)> &' for 1st argument 
    function(const function&); 
    ^
/usr/include/c++/v1/functional:1450:5: note: candidate constructor not viable: no known conversion from '(lambda at main.cpp:8:60)' to 'std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > &(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > &)> &&' for 1st argument 
    function(function&&) _NOEXCEPT; 
    ^
/usr/include/c++/v1/functional:1454:41: note: candidate template ignored: disabled by 'enable_if' [with _Fp = (lambda at main.cpp:8:60)] 
             __callable<_Fp>::value && 
             ^
main.cpp:8:60: note: candidate function 
    std::function<std::string&(std::string&)> change_str = [](std::string& str){ 
                 ^
1 error generated. 

Toutefois, si je change la déclaration de std::function-auto, il fonctionne:

auto change_str = ... 

Pourquoi le type explicite ne fonctionne pas pour lambda?

+0

Notez que 'std :: function' n'est pas" explicitement typé lambda ". Autrement dit, 'std :: function' n'est pas seulement pour lambdas (aussi, les lambdas ne sont pas spéciaux). Il peut stocker chaque 'f' qui peut être appelé comme' f (args) '. – milleniumbug

Répondre

14

Un lambda sans type de retour est auto, et supprime automatiquement la référence externe, donc vous ne renvoyez pas string& mais seulement string.

simplement déclarer fonctionnelle

std::function<std::string&(std::string&)> change_str = 
[](std::string& str) -> string& ///<--- NOTE THIS 
{ 
    return (str = "Hello world!"); 
}; 
+4

* "Un lambda sans type de retour est' auto' "* Ce qui signifie qu'en C++ 14, nous pourrions utiliser' -> decltype (auto) ', mais cela pourrait être considéré comme obscur. – dyp

+0

@dyp: le problème est que 'auto' appliqué à un' decltype' qui est une référence à ne pas ressembler à decltype d'une auto qui n'est pas en soi une référence ... Oui: c'est * obscur *, au moins jusqu'à ce qu'il devienne un idiome bien établi ... en 2017 –

4

Type de retour Déduit pour votre lambda est std::string, c'est pourquoi votre déclaration ne correspond pas. Mais lorsque vous spécifiez explicitement le type de retour, cela fonctionne:

std::function<std::string&(std::string&)> change_str = 
     [](std::string& str) -> std::string& 
{ 
    return (str = "Hello world!"); 
}; 
2

Un lambda sans type de retour se comporte comme auto qui suit les Template Argument Deduction règles et votre type de retour est déduit être std::string et non std::string&

Si la le type se spécifié explicitement tout va bien

std::function<std::string&(std::string&)> change_str = 
           [](std::string& str) -> std::string& { 
    return (str = "Hello world!"); 
}; 
2

Comme d'autres disent, la question est que la déduction de type de retour par défaut qui est std::string déduit pas com patible avec le std::string& attendu.

Catalogue des différentes déclarations pour résoudre ceci:

// be completely explicit about the return type 
[](std::string& str) -> std::string& { 

// be explicit about returning lvalue reference 
[](std::string& str) -> auto& { 

// be explicit that you're returning some kind of reference type, 
// but use reference collapsing to determine if it's an lvalue or rvalue reference 
[](std::string& str) -> auto&& { 

// use C++14 feature to deduce reference type 
[](std::string& str) -> decltype(auto) { 

Ils sont énumérés dans l'ordre moins le plus générique. Cependant, dans ce cas, il n'y a pas de besoin particulier de généricité: vous avez seulement déduit le type de retour parce que c'est le type par défaut/le moins typé. Parmi ceux-ci, je dirais probablement être explicite est probablement le meilleur: [](std::string &str) -> std::string& {

quantdev supprimé sa réponse que je pense en fait une bonne suggestion:

[](std::string& str) { 
    return std::ref(str = "Hello world!"); 
}; 

Cela fonctionne parce que std::function exige la convertibilité appropriée à/de la arguments et les types de retour, et renvoyant le résultat de std::ref répond ici à cette exigence.

Les deux utilisant std::ref et en utilisant un type de retour explicite std::string & semblent lisibles pour moi. Avec des optimisations sur ma mise en œuvre produit exactement la même chose pour les deux, donc si vous préférez le look de std::ref il y a peu de raisons de ne pas l'utiliser.

Questions connexes