
背景代码
cpp
void error_msg(std::initializer_list<std::string> il) {
for (const auto &elem : il) { // 版本1
std::cout << elem << " ";
}
for (const auto elem : il) { // 版本2
std::cout << elem << " ";
}
}
1. for (const auto &elem : il)
-
elem
的类型 :const std::string&
-
含义 :
elem
是对initializer_list
里面元素的常量引用,不会发生拷贝。 -
特点:
-
避免了拷贝,效率高。
-
无法修改
elem
(因为加了const
)。
-
2. for (const auto elem : il)
-
elem
的类型 :std::string
-
含义 :每次循环时,都会 拷贝
initializer_list
中的元素到elem
。 -
特点:
-
elem
是一个新的局部对象,和容器里的元素无关。 -
即使你去改
elem
,也不会影响il
里的内容。 -
有性能损耗(每次循环都拷贝一个字符串)。
-
直观对比图
写法 | elem 类型 |
是否拷贝 | 是否能修改 | 用途 |
---|---|---|---|---|
for (const auto &elem : il) |
const std::string& |
❌ 否 | ❌ 否 | 高效只读遍历 |
for (auto &elem : il) |
std::string& |
❌ 否 | ✅ 是 | 想要修改原元素时 |
for (const auto elem : il) |
std::string |
✅ 是 | ❌ 否 | 只读,但产生拷贝 |
for (auto elem : il) |
std::string |
✅ 是 | ✅ 是(改的是副本) | 拷贝副本后修改 |
举例演示
cpp
#include <iostream>
#include <initializer_list>
#include <string>
using namespace std;
void error_msg(initializer_list<string> il) {
cout << "[引用方式]:" << endl;
for (const auto &elem : il) {
cout << &elem << " "; // 打印引用的地址
}
cout << endl;
cout << "[拷贝方式]:" << endl;
for (const auto elem : il) {
cout << &elem << " "; // 每次都是新地址(新对象)
}
cout << endl;
}
int main() {
error_msg({"hello", "world", "C++"});
}
输出(示意)
cpp
[引用方式]:
0x7ffc8a4a9d40 0x7ffc8a4a9d48 0x7ffc8a4a9d50
[拷贝方式]:
0x7ffc8a4a9c10 0x7ffc8a4a9c20 0x7ffc8a4a9c30
👉 引用方式:多个地址紧挨着,对应 initializer_list
内部存储。
👉 拷贝方式:每次循环 elem
都是新对象,地址完全不同。
总结
-
for (const auto &elem : il)
→
elem
是const std::string&
,高效,不拷贝。 -
for (const auto elem : il)
→
elem
是std::string
,每次都会拷贝,效率低。
所以,一般遍历时推荐用 引用,除非你明确需要拷贝。