《C++ Primer 第五版》initializer_list 涉及到的范围 for 循环(range-based for) 的语义差别
背景代码
void error_msg(std::initializer_list<std::string> il) {for (const auto &elem : il) { // 版本1std::cout << elem << " ";}for (const auto elem : il) { // 版本2std::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 | ✅ 是 | ✅ 是(改的是副本) | 拷贝副本后修改 |
举例演示
#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++"});
}
输出(示意)
[引用方式]:
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
,每次都会拷贝,效率低。
所以,一般遍历时推荐用 引用,除非你明确需要拷贝。