Capture by value with reference parameter modifies captured variable outside expression?
An answer to this question on Stack Overflow.
Question
When capturing hello as a reference, I expect to be able to modify hello outside my lambda function, but the following code ends with the same hello.
#include <iostream>
#include <algorithm>
using namespace std;
int main(){
char hello[] = {"Hello, World!"};
auto up = [&, hello] (char c) {
if (!isupper(c))
{
c = toupper(c);
}
};
for_each(hello, hello + sizeof(hello), up);
cout<<hello<<endl;
}
Hello, World!
When passing hello by value with c as a reference parameter, I get my expected result.
#include <iostream>
#include <algorithm>
using namespace std;
int main(){
char hello[] = {"Hello, World!"};
auto up = [hello] (char& c) {
if (!isupper(c))
{
c = toupper(c);
}
};
for_each(hello, hello + sizeof(hello), up);
cout<<hello<<endl;
}
cout<<hello<<endl;
HELLO, WORLD!
My understanding is when hello is captured by value, up will get a local copy of hello. Implying that whether c is a reference or not hello will not be modified. But in my example c is acting as a reference to the non-local copy of hello. I feel as if I'm missing something fundamental with references.
Answer
When compiled, your lambda expressions aren't actually capturing anything, so you might as well write:
auto up = [] (char c) {
if (!isupper(c))
{
c = toupper(c);
}
};
But, even if you do use [&], how do you think the compile knows that c is part of hello? Couldn't c just as easily be part of goodbye?
You can think of using &c as telling the compiler which array c belongs to.
Also, you're working in C++, these raw arrays are not recommended there. This is, perhaps, a better way:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main(){
std::string hello = {{"Hello, World!"}};
auto up = [hello] (char& c) { c = toupper(c); };
for_each(hello.begin(), hello.end(), up);
cout<<hello<<endl;
}