Jawny zwracany typ Lambda

93

Kiedy próbuję skompilować ten kod (VS2010), pojawia się następujący błąd: 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;
  });
}

Jak określiłem, że lambda ma typ powrotu „void”. Co więcej, jak określić, że lambda ma zwracany typ „bool”?

AKTUALIZACJA

Następujące kompilacje. Czy ktoś może mi powiedzieć, dlaczego to się kompiluje, a drugie nie?

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
źródło
6
Możesz to wyraźnie określić za pomocą ->, np.[&](double d) -> double { //...
Flexo
2
Radziłbym po prostu niejawnie przechwytywać potrzebne zmienne (tylko [&]...), ponieważ to, co obecnie masz, jest niepotrzebnie rozwlekłe.
Xeo
2
[&expression, &start, &end, &what, &flags]...(twój) vs [&]...(mój). Teraz powiedz mi, kto jest bardziej szczegółowy. ;) [&]mówi lambdzie, aby przechwyciła wszystko, czego używasz wewnątrz ciała lambda, przez odniesienie. Nazywa się to „domyślnym przechwytywaniem”. Drugi jest [=]i będzie przechwytywany przez kopię.
Xeo
1
@Xeo, Effective Modern C ++, pozycja 31, zaleca jawne przechwytywanie, aby uniknąć wiszących odwołań. Ugryzło mnie to kilka razy jako kara za lenistwo… eee, zwięzłość. :-)
Emile Cormier
2
Nawiasem mówiąc, ograniczenia są zmniejszane w przypadku wyprowadzonych lambd typu zwracanego w C ++ 14. Typy zwracane można wywnioskować dla wyrażeń lambd z więcej niż jedną instrukcją w treści, a jeśli wyrażenie każdej instrukcji return ma ten sam typ, można teraz uzyskać wywnioskowany typ zwracany z wieloma instrukcjami powrotu.
Anthony Hall

Odpowiedzi:

194

Możesz jawnie określić zwracany typ lambdy, używając -> Typepo liście argumentów:

[]() -> Type { }

Jeśli jednak lambda ma jedną instrukcję, a ta instrukcja jest instrukcją powrotu (i zwraca wyrażenie), kompilator może wydedukować typ zwracania na podstawie typu tego zwróconego wyrażenia. Masz wiele instrukcji w swojej lambdzie, więc nie dedukuje typu.

Seth Carnegie
źródło
4
kompilator może to zrobić, ale standard zabrania tego.
Johannes Schaub - litb
10
-1: To nie jest błąd kompilatora. Norma jest w tym bardzo jasna: sekcja 5.1.2, paragraf 4 określa, w jaki sposób dokonuje się odliczenia i na jakich warunkach.
Nicol Bolas
2
Chociaż jest to niedozwolone zgodnie z najnowszą wersją roboczą, mogłem stwierdzić, że wygląda na to, że jest faktycznie dozwolone w ostatecznej specyfikacji w komentarzu do tej łatki gcc.gnu.org/ml/gcc-patches/2011-08/msg01901.html . Czy ktoś ma ostateczną specyfikację do zweryfikowania?
Eelke
2
Dużo używałem wyrażeń lambda i ani razu nie podałem wyraźnie typu zwracanego. Odliczenie typu zwracanego (przynajmniej pod VS2012 i VS2013) działa bezbłędnie, nawet jeśli w wyrażeniu lambda jest więcej niż jedna instrukcja return. Oczywiście różne instrukcje return muszą pasować do tego samego wyrażenia lambda. Np. Instrukcja taka jak "auto f = [] (int i) {if (i> 5) return true; return false;};" kompiluje się bez problemu i jeśli wywołasz "auto b = f (10);" b będzie typu bool i oczywiście będzie prawdziwe;
sprite
1
return nullptr;może rzucać kluczem w dedukcji typu, nawet jeśli jest to poprawne, ponieważ w przeciwnym razie zwracany jest typ wskaźnika.
Grault
16

Zwracany typ lambda (w C ++ 11) można wywnioskować, ale tylko wtedy, gdy istnieje dokładnie jedna instrukcja, a ta instrukcja jest returninstrukcją, która zwraca wyrażenie (na przykład lista inicjalizacyjna nie jest wyrażeniem). Jeśli masz lambdę składającą się z wielu instrukcji, zakłada się, że zwracany typ jest nieważny.

Dlatego powinieneś to zrobić:

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

Ale tak naprawdę twoje drugie wyrażenie jest o wiele bardziej czytelne.

Nicol Bolas
źródło
Niezły przykład; czubek: Czy );na końcu brakuje wywołania funkcji ?
kevinarpe
6

Możesz mieć więcej niż jedną instrukcję, gdy nadal zwracasz:

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

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

Valen
źródło
4
przecinek to odrażający operator. dezorientuje ludzi, którzy nie są świadomi jego istnienia ani poziomu pierwszeństwa. nigdy nie ma ważnego użycia ani imo. zawsze można tego uniknąć dzięki większej liczbie funkcji lub lepszej organizacji kodu.
jheriko
@jheriko zgadzam się, istnienie mojej odpowiedzi jest tylko dla tych, którzy naprawdę chcą niezależnego, jednowierszowego rozwiązania XD (to wciąż jedna linia, prawda?). Przecinek jest naprawdę niezauważalny i nikt nigdy nie umieściłby całej głównej metody w tej formie.
Valen,
1
Oczywiście, z pewnością udzielasz prawidłowej odpowiedzi, po prostu nie jestem fanem robienia czegokolwiek, aby zachęcić lub nawet zademonstrować złe praktyki. kiedy ludzie dowiedzą się, że przecinek jest operatorem, to odliczanie do momentu, gdy zaczną go nadużywać i dłuższe, dopóki nie nauczą się lepiej. :)
jheriko
@jheriko Widziałem kiedyś, że został on użyty w interesujący sposób na liście inicjalizacji członków, ale to było tylko po to, aby się bawić, jeśli dobrze pamiętam.
Justin Time - Przywróć Monikę