Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I have this code:

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;

It does not compile, and say:

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.

However if I change the declaration of std::function to auto, then it works:

auto change_str = ...

Why is the explicit type not working for lambda?

share|improve this question
    
Note that std::function is not "explicitly typed lambda". That is, std::function is not just for lambdas (also, lambdas aren't special). It can store every f that can be called like f(args). –  milleniumbug yesterday

4 Answers 4

up vote 13 down vote accepted

A lambda with no return type is auto, and auto remove the external reference, so you are not returning string& but just string.

Just declare the functional as

std::function<std::string&(std::string&)> change_str = 
[](std::string& str) -> string&  ///<--- NOTE THIS
{
    return (str = "Hello world!");
};
share|improve this answer
4  
"A lambda with no return type is auto" Which means, in C++14, we could use -> decltype(auto), but that could be considered obscure. –  dyp yesterday
    
@dyp: the problem is that auto applied to a decltype that is a reference in not like decltype of an auto that's not by itself a reference... Yes: it's obscure, at least until it will become a well established idiom... in 2017 –  Emilio Garavaglia yesterday

Deduced return type for your lambda is std::string, that's why your declaration does not match. But when you explicitly specify return type, it works:

std::function<std::string&(std::string&)> change_str = 
        [](std::string& str) -> std::string& 
{
    return (str = "Hello world!");
};
share|improve this answer

A lambda with no return type behaves as auto which follows the Template Argument Deduction rules and your return type is deduced to be std::string and not std::string&

If the type gets specified explicitly everything's fine

std::function<std::string&(std::string&)> change_str = 
                                 [](std::string& str) -> std::string& {
  return (str = "Hello world!");
};
share|improve this answer

As others say, the issue is that the default return type deduction deduces std::string which is not compatible with the expected std::string&.

Catalogue of the various declarations to solve this:

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

These are listed in order of least to most generic. However in this case there's no particular need for genericity: you were only deducing the return type because that's the default/least typing. Of these I'd probably say being explicit is probably the best: [](std::string &str) -> std::string& {

quantdev deleted his answer which I think makes another good suggestion:

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

This works because std::function only requires suitable convertibility to/from the argument and return types, and returning the result of std::ref here meets that requirement.

Both using std::ref and using an explicit std::string & return type seem readable to me. With optimizations on my implementation produces exactly the same thing for both, so if you prefer the look of std::ref there's little reason not to use it.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.