2014-09-17 13 views
11

ho questo codice:Non è possibile utilizzare in modo esplicito digitato 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; 

E non compila, e dico:

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. 

Tuttavia se cambio la dichiarazione di std::function-auto, allora funziona:

auto change_str = ... 

Perché il tipo esplicito non funziona per lambda?

+0

Si noti che 'std :: function' non è" lambda esplicitamente digitato ". Cioè, 'std :: function' non è solo per lambda (anche, i lambda non sono speciali). Può memorizzare ogni 'f' che può essere chiamato come' f (args) '. – milleniumbug

risposta

14

Un lambda senza tipo di ritorno è auto e rimuove automaticamente il riferimento esterno, pertanto non si restituisce string& ma solo string.

dichiarare la funzionale come

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

* "Un lambda senza tipo di ritorno è' auto' "* Il che significa che, in C++ 14, potremmo usare' -> decltype (auto) ', ma che potrebbe essere considerato oscuro. – dyp

+0

@dyp: il problema è che 'auto' si applica a un' decltype' che è un riferimento non come decltype di un'auto che non è di per sé un riferimento ... Sì: è * oscuro *, almeno finché non diventerà un idioma ben consolidato ... nel 2017 –

4

tipo di ritorno dedotta per la vostra lambda è std::string, è per questo che la sua dichiarazione non corrisponde. Ma quando si specifica in modo esplicito tipo di ritorno, funziona:

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

Un lambda senza alcun tipo di ritorno si comporta come auto che segue le Template Argument Deduction regole e il tipo di ritorno è dedotto di essere std::string e non std::string&

Se il tipo viene specificato in modo esplicito va tutto bene

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

Come altri dire, il problema è che il tipo di ritorno di default deduzione deduce std::string che non è com patibile con il previsto std::string&.

Catalogo delle varie dichiarazioni per risolvere questo:

// 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) { 

Questi sono elencati in ordine di meno al più generico. Tuttavia, in questo caso non è necessario alcun tipo di genericità: si stava solo deducendo il tipo di reso perché quello è il valore predefinito/la digitazione minima. Di questi probabilmente sarei dire essere esplicito è probabilmente il migliore: [](std::string &str) -> std::string& {

quantdev cancellato la sua risposta che mi pare fa un altro buon suggerimento:

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

Questo funziona perché std::function richiede solo la convertibilità adatto da/per la argomento e tipi di ritorno, e restituendo il risultato di std::ref qui soddisfa tale requisito.

Entrambe utilizzando std::ref e utilizzando un tipo di reso esplicito std::string & mi sembrano leggibili. Con ottimizzazioni sulla mia implementazione produce esattamente la stessa cosa per entrambi, quindi se preferisci l'aspetto di std::ref non c'è motivo di non usarlo.

Problemi correlati