104
votes

When I try and compile this code (VS2010) I am getting the following error: error C3499: a lambda that has been specified to have a void return type cannot return a value

void DataFile::removeComments()
{
  string::const_iterator start, end;
  boost::regex expression("^\\s?#");
  boost::match_results<std::string::const_iterator> what;
  boost::match_flag_type flags = boost::match_default;
  // Look for lines that either start with a hash (#)
  // or have nothing but white-space preceeding the hash symbol
  remove_if(rawLines.begin(), rawLines.end(), [&expression, &start, &end, &what, &flags](const string& line)
  {
    start = line.begin();
    end = line.end();
    bool temp = boost::regex_search(start, end, what, expression, flags);
    return temp;
  });
}

How did I specify that the lambda has a 'void' return type. More-over, how do I specify that the lambda has 'bool' return type?

UPDATE

The following compiles. Can someone please tell me why that compiles and the other does not?

void DataFile::removeComments()
{
  boost::regex expression("^(\\s+)?#");
  boost::match_results<std::string::const_iterator> what;
  boost::match_flag_type flags = boost::match_default;
  // Look for lines that either start with a hash (#)
  // or have nothing but white-space preceeding the hash symbol
  rawLines.erase(remove_if(rawLines.begin(), rawLines.end(), [&expression, &what, &flags](const string& line)
  { return boost::regex_search(line.begin(), line.end(), what, expression, flags); }));
}
3
You can explicitly specify it with ->, e.g. [&](double d) -> double { //... - Flexo♦
I'd advise you to just implicitly capture the variables you need (only [&]...), as what you currently have is needlessly verbose. - Xeo
[&expression, &start, &end, &what, &flags]... (yours) vs [&]... (mine). Now tell me whose is more verbose. ;) [&] tells the lambda to capture everything that you use inside the lambda body, by reference. It's called a "capture default". The other one is [=] and will capture by copy. - Xeo
@Emile: That only matters for lambdas that outlive the current scope in some form or other, really, which is certainly not the case here. - Xeo
By the way, the constraints are reduced on deduced return-type lambdas in C++14. Return types can be deduced for lambdas with more than one statement in the body, and as long as the expression of each return statement has the same type, you can now have a deduced return type with multiple return statements. - Anthony Hall

3 Answers

214
votes

You can explicitly specify the return type of a lambda by using -> Type after the arguments list:

[]() -> Type { }

However, if a lambda has one statement and that statement is a return statement (and it returns an expression), the compiler can deduce the return type from the type of that one returned expression. You have multiple statements in your lambda, so it doesn't deduce the type.

18
votes

The return type of a lambda (in C++11) can be deduced, but only when there is exactly one statement, and that statement is a return statement that returns an expression (an initializer list is not an expression, for example). If you have a multi-statement lambda, then the return type is assumed to be void.

Therefore, you should do this:

  remove_if(rawLines.begin(), rawLines.end(), [&expression, &start, &end, &what, &flags](const string& line) -> bool
  {
    start = line.begin();
    end = line.end();
    bool temp = boost::regex_search(start, end, what, expression, flags);
    return temp;
  })

But really, your second expression is a lot more readable.

6
votes

You can have more than one statement when still return:

[]() -> your_type {return (
        your_statement,
        even_more_statement = just_add_comma,
        return_value);}

http://www.cplusplus.com/doc/tutorial/operators/#comma