Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FizzBuzz.cpp with lambdas?

Tags:

c++

c++11

lambda

I'm trying to write FizzBuzz in C++11 using lambdas, but I'm getting a weird compiler error.

Code:

#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
like image 989
mcandre Avatar asked Mar 19 '13 20:03

mcandre


2 Answers

You should capture by reference instead of capturing by value in your lambda:

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

That also happens to solve the problem - the call operator of a generated lambda closure is marked as const by default.


NOTE:

Another way to make this compile is to use the mutable keyword, as in the snippet below:

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

The mutable keyword has the effect of dropping the const in the call operator of the generated lambda closure.

However, I do believe you really do not want this: why modifying strings in an array that you will forget about when the function returns?

Capturing by reference will solve your problem.


UPDATE:

As pointed out by Daniel Frey in the comments, this instruction:

list<int> range(0, 100);

Will create a list of size zero, whose elements (whose zero elements) are all initialized with the value 100. Probably not what you want. You may want to change it into something like the following (std::iota is only available if you are working with C++11, otherwise you'll have to unroll your own loop of assignments):

#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
like image 191
Andy Prowl Avatar answered Nov 16 '22 02:11

Andy Prowl


This change should fix it, so you capture by reference:

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

Also as Daniel and Andy point out your initialization of range is probably not what you expect since it creates a zero sized list:

list<int> range(0, 100);
like image 28
Shafik Yaghmour Avatar answered Nov 16 '22 01:11

Shafik Yaghmour