Tipo de retorno explícito de lambda

91

Quando tento compilar este código (VS2010), recebo o seguinte erro: 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;
  });
}

Como especifiquei que o lambda tem um tipo de retorno 'void'. Além disso, como posso especificar que o lambda tem o tipo de retorno 'bool'?

ATUALIZAR

O seguinte compila. Alguém pode me dizer por que isso compila e o outro não?

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); }));
}
Ryan
fonte
6
Você pode especificá-lo explicitamente com ->, por exemplo[&](double d) -> double { //...
Flexo
2
Aconselho você apenas a capturar implicitamente as variáveis ​​de que precisa (apenas [&]...), pois o que você tem atualmente é desnecessariamente detalhado.
Xeo
2
[&expression, &start, &end, &what, &flags]...(seu) vs [&]...(meu). Agora me diga de quem é mais prolixo. ;) [&]diz ao lambda para capturar tudo o que você usa dentro do corpo do lambda, por referência. É chamado de "captura padrão". O outro é [=]e irá capturar por cópia.
Xeo
1
@Xeo, Effective Modern C ++, Item 31, recomenda capturar explicitamente, para evitar referências pendentes. Já fui mordido por isso algumas vezes, como punição por ser preguiçoso ... er, conciso. :-)
Emile Cormier
2
A propósito, as restrições são reduzidas em lambdas de tipo de retorno deduzido em C ++ 14. Os tipos de retorno podem ser deduzidos para lambdas com mais de uma instrução no corpo e, desde que a expressão de cada instrução de retorno tenha o mesmo tipo, agora você pode ter um tipo de retorno deduzido com várias instruções de retorno.
Anthony Hall de

Respostas:

188

Você pode especificar explicitamente o tipo de retorno de um lambda usando -> Typeapós a lista de argumentos:

[]() -> Type { }

No entanto, se um lambda tem uma instrução e essa instrução é uma instrução de retorno (e retorna uma expressão), o compilador pode deduzir o tipo de retorno do tipo daquela expressão retornada. Você tem várias instruções em seu lambda, então ele não deduz o tipo.

Seth Carnegie
fonte
4
o compilador pode fazer isso, mas o padrão o proíbe de fazer isso.
Johannes Schaub - litb
9
-1: Este não é um bug do compilador. O padrão é muito claro sobre isso: a seção 5.1.2, parágrafo 4, estabelece como a dedução é feita e em que condições.
Nicol Bolas
2
Embora não seja permitido de acordo com o último rascunho, posso achar que parece que é realmente permitido nas especificações finais pelo comentário para este patch gcc.gnu.org/ml/gcc-patches/2011-08/msg01901.html . Alguém tem a especificação final para verificar?
Eelke
2
Usei expressões lambda extensivamente e nenhuma vez afirmei explicitamente o tipo de retorno. A dedução do tipo de retorno (pelo menos em VS2012 e VS2013) funciona perfeitamente mesmo se houver mais de uma instrução de retorno na expressão lambda. Obviamente, as várias instruções de retorno precisam corresponder à mesma expressão lambda. Por exemplo, uma declaração como "auto f = [] (int i) {if (i> 5) retorna verdadeiro; retorna falso;};" compila sem problemas e se você chamar "auto b = f (10);" b será do tipo bool e, é claro, verdadeiro;
sprite
1
return nullptr;pode lançar uma chave na dedução de tipo, mesmo que seja válido, pois qualquer tipo de ponteiro é retornado de outra forma.
Grault
16

O tipo de retorno de um lambda (em C ++ 11) pode ser deduzido, mas apenas quando há exatamente uma instrução, e essa instrução é uma returninstrução que retorna uma expressão (uma lista de inicializadores não é uma expressão, por exemplo). Se você tiver um lambda com várias instruções, o tipo de retorno será considerado nulo.

Portanto, você deve fazer isso:

  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;
  })

Mas, realmente, sua segunda expressão é muito mais legível.

Nicol Bolas
fonte
Bom exemplo; nitpick: sua chamada de função está faltando );no final?
kevinarpe
6

Você pode ter mais de um extrato quando ainda retornar:

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

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

Valen
fonte
4
a vírgula é uma operadora revoltante. isso confunde as pessoas que não estão cientes de sua existência ou nível de precedência. nunca há um uso válido quer imo. sempre pode ser evitado com mais funções ou código mais bem organizado.
jheriko
@jheriko concorda, a existência da minha resposta é apenas para aqueles que realmente querem uma solução XD independente de uma linha (ainda é uma linha, certo?). A vírgula realmente não é perceptível e ninguém jamais colocaria todo o método principal desta forma.
Valen,
1
claro, você certamente está dando uma resposta válida, simplesmente não sou fã de fazer nada para encorajar ou mesmo demonstrar práticas inadequadas. uma vez que as pessoas aprendem que a vírgula é um operador, é uma contagem regressiva até que comecem a abusar dela e mais longa até que aprendam melhor. :)
jheriko
@jheriko Já vi isso ter um uso interessante em uma lista de inicialização de membros uma vez, mas foi apenas para brincar, se bem me lembro.
Justin Time - Reintegrar Monica em