非成员begin()和end()函数在C++中的使用

C++标准模板库(STL)中,几乎所有容器都提供了非静态成员函数begin()和end(),它们返回指向容器起始和结束的迭代器。这使得遍历容器变得非常直观。例如,使用std::vector时,可以这样遍历:

#include <iostream> #include <vector> int main() { std::vector<int> v; for (auto it = v.begin(); it != v.end(); ++it) { std::cout << *it << std::endl; } return 0; }

然而,并非所有用户定义的容器都实现了begin()和end(),这限制了它们与STL算法或其他需要迭代器的用户定义模板函数的兼容性。尤其是使用C数组时,与使用vector相比,使用标准算法的方式大不相同。

#include <iostream> #include <algorithm> #include <vector> int inc(int n) { return n + 1; } int main() { int a[] = {1, 2, 3, 4, 5}; std::transform(&a[0], &a[0] + sizeof(a)/sizeof(a[0]), &a[0], inc); std::vector<int> v(&a[0], &a[0] + sizeof(a)/sizeof(a[0])); std::transform(v.begin(), v.end(), v.begin(), inc); return 0; }

非成员版本的begin()和end()方法是可扩展的,这意味着它们可以为任何类型(包括C数组)重载。Herb Sutter在他的文章《Elements of ModernC++Style》中主张,应该始终优先使用非成员版本。它们促进了一致性,允许更通用的编程。

总是使用非成员begin(x)和end(x)(而不是x.begin()和x.end()),因为begin(x)和end(x)是可扩展的,并且可以适应所有容器类型——甚至是数组——而不仅仅是遵循STL风格的容器,它们提供了x.begin()和x.end()成员函数。

#include <iostream> #include <vector> #include <algorithm> int main() { std::vector<int> v {1, 2, 3, 4, 5}; for (auto it = begin(v); it != end(v); ++it) { std::cout << *it << std::endl; } std::transform(begin(v), end(v), begin(v), [](int n) { return n + 1; }); return 0; }

对于C数组,可以这样重载begin()和end():

template <typename T, size_t size> T* begin(T (&c)[size]) { return &c[0]; } template <typename T, size_t size> T* end(T (&c)[size]) { return &c[0] + size; }

有了这些重载,可以这样写:

#include <iostream> #include <algorithm> int main() { int a[] = {1, 2, 3, 4, 5}; std::transform(begin(a), end(a), begin(a), [](int n) { return n + 1; }); for (auto it = begin(a); it != end(a); ++it) { std::cout << *it << std::endl; } return 0; }

如果认为非成员begin()和end()破坏了封装性,那么建议阅读Scott Meyers的《How Non-Member Functions Improve Encapsulation》,其中他解释了相反的观点。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485