用range for 改变字符串中的字符,为啥循环变量一定要用引用形式

用range for 改变字符串中的字符,为什么循环变量一定要用引用形式?
C++ Primer 上给的例题是这样,目的是把字符串中的字母都变成大写输出:

string s("Hello World!!!");
for (auto &c : s)
    c = toupper(c);
cout << s << endl;

输出结果:
用range for 改变字符串中的字符,为啥循环变量一定要用引用形式

但为什么一定要用引用形式,为什么不能这样:

#include <iostream>
#include <string>
using namespace std;

int main()
{
  string s("Hello World!");
  for (auto c : s)
     cout << toupper(c);
  return 0;
}

编译后运行的结果是:
用range for 改变字符串中的字符,为啥循环变量一定要用引用形式
输出的结果是各个字母的数字值。
H  e  l  l  o     W  o  r  l  d  !
72 69 76 76 79 32 87 79 82 76 68 33

或者把原码改成这样:

#include <iostream>
#include <string>
using namespace std;

int main()
{
  string s("Hello World!");
  for (auto c : s)
     c = toupper(c);
  cout << s << endl;
  return 0;
}


编译运行输出原字符串,根本没有变大写:
用range for 改变字符串中的字符,为啥循环变量一定要用引用形式

我不理解,引用是用另一个名字来操作变量,用变量本来的名字也可以操作变量啊,为什么在这里就不行呢?怎么解释后两次输出的结果呢?


c++ range for reference

------解决方案--------------------
先说第三种情况,你是依次把字符串s中的每个字符复制一个,并把复制得到的副本变成了大写,但你选择输出的是原来的字符串s,当然还是小写。
第二种情况,由于 C++ 是继承于 C 语言的原因,char 还是可以被当作 int 使用的,明显你这里的 auto 把类型推导成了整型。
------解决方案--------------------
auto c 推导为 char 无异议。但是 toupper 的返回值是 int 类型,cout << toupper 的时候,调用 operator<<(int) 的重载,所以当整形输出了。c=toupper; cout<<c; 的时候,因为 c 是 char,所以从 toupper 的返回类型 (int) 经类型转换又变回 char 了,后面仍然调用 operator<<(char) 的重载。