2013-03-19 11 views
5

Sto provando a scrivere FizzBuzz in C++ 11 usando lambdas, ma sto ricevendo uno strano errore del compilatore.FizzBuzz.cpp con lambdas?

Codice:

#include <iostream> 
#include <string> 
#include <sstream> 
#include <list> 
#include <algorithm> 
using namespace std; 

string fizzy(int n) { 
  int a = n % 3, b = n % 5; 

  if (a == 0 && b == 0) { 
    return "FizzBuzz"; 
  } 
  else if (a == 0) { 
    return "Fizz"; 
  } 
  else if (b == 0) { 
    return "Buzz"; 
  } 
  else { 
    stringstream out; 
    out << n; 
    return out.str(); 
  } 
} 

void fizzbuzz() { 
  string strings[100]; 
  list<int> range(0, 100); 

  for_each(range.begin(), range.end(), [=](int i) { 
      strings[i] = fizzy(i); 
    }); 

  for_each(range.begin(), range.end(), [=](int i) { 
      cout << strings[i] << endl; 
    }); 
} 

int main() { fizzbuzz(); } 

Trace:

$ make 
g++ -std=c++0x -o fizzy fizzy.cpp 
fizzy.cpp: In lambda function: 
fizzy.cpp:32:27: error: passing 'const std::string' as 'this' argument of 'std::basic_string<_CharT, 
 _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(std::basic_string<_CharT, _ 
Traits, _Alloc>&&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<ch 
ar>, std::basic_string<_CharT, _Traits, _Alloc> = std::basic_string<char>]' discards qualifiers 
make: *** [fizzy] Error 1 
+6

'lista intervallo (0, 100);' fa * non * fa quello che pensi che faccia. –

+1

Questo deve essere l'uso più inefficiente di 'for_each' ever. –

+0

Le acquisizioni di valore sono costanti a meno che non si effettui il muting della lambda'. –

risposta

10

Si dovrebbe cattura con riferimento invece di catturare dal valore nella vostra lambda:

for_each(range.begin(), range.end(), [&](int i) { 
//         ^
    strings[i] = fizzy(i); 
    }); 

che avviene anche per risolvere il problema: l'operatore di chiamata di un agnello generato La chiusura è contrassegnata come const per impostazione predefinita.


NOTA:

Un altro modo per rendere questa compilazione è quello di utilizzare la parola chiave mutable, come nel frammento di seguito:

for_each(range.begin(), range.end(), [=](int i) mutable { 
//            ^^^^^^^ 
    strings[i] = fizzy(i); 
    }); 

Il mutable parola chiave ha l'effetto di far cadere il const nell'operatore di chiamata della chiusura lambda generata.

Tuttavia, credo davvero fare non vuole questo: perché modifica le stringhe in una matrice che si dimentica circa quando la funzione ritorna?

La cattura tramite riferimento risolverà il problema.


UPDATE:

Come sottolineato da Daniel Frey nei commenti, questa istruzione:

list<int> range(0, 100); 

creerà un elenco di dimensioni pari a zero, i cui elementi (la cui a zero elementi) sono tutti inizializzati con il valore 100. Probabilmente non quello che vuoi. Si consiglia di trasformarla in qualcosa di simile alla seguente (std::iota è disponibile solo se si sta lavorando con C++ 11, altrimenti dovrete srotolare il proprio ciclo di assegnazioni):

#include <algorithm> 

list<int> range(100); // Creates a list of 100 elements 
iota(begin(range), end(range), 0); // Assigns value 0..99 to those elements 
+2

'mutable' da solo non risolverà il problema,' stringhe' è un array. – Praetorian

+0

@Praetorian: Sembra compilare [qui] (http://liveworkspace.org/code/yJL1f$27) sebbene ... –

+0

Come mai un array è riproducibile? Forse il valore catturato è dopo l'array di puntare la conversione implicita? Inoltre, dall'errore VS2012 * C3478: "stringhe": un array non può essere catturato per valore *. Ma tutti e 2 i compilatori su LWS lo compilano. – Praetorian

1

Questo cambiamento dovrebbe risolvere il problema, in modo da catturare con riferimento:

for_each(range.begin(), range.end(), [&strings](int i) { 
    strings[i] = fizzy(i); 
}); 

anche come Daniel e Andy sottolineano la vostra inizializzazione della range non è probabilmente quello che ci si aspetta in quanto crea una lista zero dimensioni:

list<int> range(0, 100); 
Problemi correlati