《C++ Primer 第五版》initializer_list 涉及到的范围 for 循环(range-based for) 的语义差别

背景代码

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)

    elemconst std::string&,高效,不拷贝。

  • for (const auto elem : il)

    elemstd::string,每次都会拷贝,效率低。

所以,一般遍历时推荐用 引用,除非你明确需要拷贝。