vector::begin()和std::begin()之间的区别

57

在c++中迭代vector时,我注意到标准库中有一个begin()函数,而vector类也有一个begin()成员函数。这两者有什么区别(如果有的话),应该使用哪个?

示例:

vector<int> numbers;
//Code to put values in my vector
for (vector<int>::iterator i = numbers.begin(); i < numbers.end(); i++)
    cout << *i << '\n';

对比:

vector<int> numbers;
//Code to put values in my vector
for (vector<int>::iterator i = std::begin(numbers); i < std::end(numbers); i++)
    cout << *i << '\n';

1
对于容器来说,它们是相同的。但是 std::begin 也适用于内置数组,在模板函数中非常有用。 - SwiftMango
2
不过,++i 可能比 i++ 更高效,因为后者会返回一个迭代器的副本。 - M.M
1
这个回答解决了您的问题吗?为什么在C++11中使用非成员begin和end函数? - Thomas Weller
3个回答

56

std::begin()在C++11中添加,旨在使编写通用代码更加容易(例如在模板中)。最明显的原因是,普通的C风格数组没有方法,因此没有.begin()。因此,您可以与C风格数组一起使用std::begin(),以及具有其自己的begin()end()的STL样式容器。

如果您正在编写非模板代码,则可以忽略std::begin();如果您突然开始在任何地方都使用它,您的同事程序员可能会觉得奇怪,因为它是新的。


4
仅仅因为你想保持一致性而使用它,怎么样? - Benjamin Lindley
1
@BenjaminLindley:我不认为这正是@RalphWaldoEmerson说的“愚蠢的一致性是小心灵的魔怪”,但我认为如果我们已经用了几十年的旧方法,没有理由使用std :: begin()会分散注意力。这需要更多的打字,没有任何好处。 - John Zwinck
3
@JohnZwinck 的 begin(vec)(感谢 ADL)比 vec.begin() 更省敲键盘的时间 :) - T.C.
4
考虑到如果std::begin可用,那么范围-based for 循环和auto也就随之而来了;因此我宁愿完全不看第二个版本! - M.M
1
@MattMcNabb:这是一个很好的观点,在C++11中,许多迭代器的用法已经不再需要了。 - John Zwinck
显示剩余3条评论

6

对于向量,std::begin()的实现仅仅调用std::vector<T>::begin(),因此在这种情况下两者没有区别。

std::begin()真正发挥作用的是在泛型算法中:

template<typename Container>
void my_algorithm(Container c) {
    using std::begin;
    using std::end;
    auto const start = begin(c);  // ADL will find the appropriate overload
    auto const finish = end(c);
    // ... rest of code ...
}

0

如果你的编译器优化了你的代码,那么你使用哪个都无所谓。否则,你可能想使用更具面向对象的格式:numbers.begin()

这段代码已经被插入到Quick Bench中进行速度测量:

std::vector<int> arr = {0,1,2};

static void formatA(benchmark::State& state) {
  for (auto _ : state) { // Code inside this loop is measured repeatedly
    std::vector<int>::iterator itrA = arr.begin(); // arr.end();
    benchmark::DoNotOptimize(itrA);
  }
}
// Register the function as a benchmark
BENCHMARK(formatA);

static void formatB(benchmark::State& state) {
  for (auto _ : state) { // Code before the loop is not measured
    std::vector<int>::iterator itrB = begin(arr); // end(arr);
    benchmark::DoNotOptimize(itrB);
  }
}
BENCHMARK(formatB);

所有编译都使用STL=libstdc++(GNU),begin()的结果如下:

参数 \ CPU 时间(4位小数) 格式A 格式B 结论
优化 = 无
编译器 = Clang 11.0
标准 = c++11
2.1914 5.1870 A 比 B 快 2.4 倍
优化 = 无
编译器 = Clang 15.0
标准 = c++20
2.0666 2.8974 A 比 B 快 1.4 倍
优化 = O3
编译器 = Clang 11.0
标准 = c++11
1.1094 1.0130 大致等效的运行时间
(等效汇编)
优化 = O3
编译器 = Clang 15.0
标准 = c++20
1.0093 1.0007 大致等效的运行时间
(等效汇编)

而对于 end()

参数 \ CPU 时间 (4dp) 格式A 格式B 结论
优化 = 无
编译器 = Clang 11.0
标准 = c++11
2.5166 2.6341 运行时间大致相等
优化 = 无
编译器 = Clang 15.0
标准 = c++20
2.3657 3.8461 A 比 B 快 1.6 倍
优化 = O3
编译器 = Clang 11.0
标准 = c++11
1.0045 1.0126 运行时间大致相等
(汇编代码相同)
优化 = O3
编译器 = Clang 15.0
标准 = c++20
1.0047 1.0012 运行时间大致相等
(汇编代码相同)

在没有进行优化的情况下,
格式A会分别调用<std::vector<int, std::allocator<int> >::begin()>end(),而
格式B会调用<decltype (({parm#1}.begin)()) std::begin<std::vector<int, std::allocator<int> > >(std::vector<int, std::allocator<int> >&)>end()。通过推导数据类型来确定格式B的时间会被浪费。

在进行优化的情况下,编译器已经完成了类型推导,两者将使用以下汇编代码,除了地址之外不会有任何变化:

mov    0x3c5b1(%rip),%rax        # 24f188 <arr>
mov    %rax,0x8(%rsp)
add    $0xffffffffffffffff,%rbx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接