std::begin和std::end适用于所有提供成员或非成员begin()/end()函数的类型,包括标准容器、c风格数组、initializer_list等;不支持裸指针或未特化自定义类。

std::begin 和 std::end 能用在哪些容器上
它们不是只给 std::vector 或 std::array 准备的,而是泛化的访问接口:只要类型支持 begin() / end() 成员函数(比如所有标准容器),或定义了对应的非成员 begin() / end()(比如原生数组、std::initializer_list),std::begin 和 std::end 就能工作。
常见可用类型包括:std::vector、std::string、std::array、std::list、std::map、C 风格数组(如 int arr[5])、std::initializer_list。不支持的典型例子是裸指针(如 int*)或自定义类(除非你显式提供非成员 begin/end)。
为什么不用容器自己的 begin()/end(),而要用 std::begin/std::end
关键在于统一接口——尤其是处理原生数组时,它没有成员函数,但 std::begin(arr) 却能返回合法迭代器;而对容器对象调用 std::begin(c),底层其实就转发到 c.begin(),行为一致。
这使得模板代码可以写得更通用:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
template <typename c>
void print_size(const C& c) {
auto first = std::begin(c);
auto last = std::end(c);
std::cout <p>上面函数既能传 <code>std::vector<int></int></code>,也能传 <code>int arr[10]</code>,无需重载或特化。</p>
<p>容易踩的坑:<br>
- 对 const 容器,<code>std::begin</code> 返回 <code>const_iterator</code>,但如果你误用了非 const 版本的变量接收(比如声明为 <code>auto&</code> 并试图修改),编译会失败。<br>
- 在函数模板中若未包含 <code><iterator></iterator></code>,<code>std::begin</code> 可能不可见(C++11 起定义在该头文件中)。</p>
<h3>std::begin/std::end 和范围 for 循环的关系</h3>
<p>范围 for 底层就是靠 <code>std::begin</code>/<code>std::end</code> 实现的——你写 <code>for (auto& x : container)</code>,编译器实际展开为类似 <code>for (auto it = std::begin(container); it != std::end(container); ++it)</code> 的逻辑。</p>
<p>这意味着:<br>
- 如果你自定义类型想支持范围 for,必须让 <code>std::begin</code>/<code>std::end</code> 可调用(通过成员或非成员函数)<br>
- 若你禁用了 ADL(比如在局部作用域 using 了错误命名空间),范围 for 可能意外失效<br>
- 不要试图在范围 for 内部修改容器大小(如 <code>push_back</code>),这会让 <code>std::end</code> 返回的迭代器失效,行为未定义</p>
<h3>std::begin/std::end 的 const 与引用语义细节</h3>
<p>它们严格按参数类型推导返回类型:<br>
- 参数是 <code>const C&</code> → 返回 <code>const_iterator</code>(或 <code>const T*</code>)<br>
- 参数是 <code>C&&</code>(右值引用)→ 多数标准容器不支持移动后取迭代器,通常应避免对临时对象调用 <code>std::begin</code><br>
- 数组类型推导精确:<code>int a[3]</code> → <code>std::begin(a)</code> 返回 <code>int*</code>,不是 <code>const int*</code></p>
<p>一个易忽略点:<code>std::begin</code> 对 <code>std::string</code> 返回的是 <code>std::string::iterator</code>,但 C++11 中 <code>std::string</code> 的存储不一定连续(直到 C++11 后才保证),所以不要假设 <code>&*std::begin(s)</code> 一定等于 <code>&s[0]</code>(尽管实践中几乎总是成立)。</p>
<p>真正麻烦的边界情况是自定义类型 + 模板 + 移动语义混合时,<code>std::begin(std::move(x))</code> 可能触发非预期的重载解析,建议除非明确需要,否则只对左值使用。</p></typename>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










