Reputation: 4016
While iterating over a vector in c++, I noticed there is a begin()
function in the standard library, and also a begin()
as a member function of the vector
class. What, if any, is the difference between the two, and which should be used over the other?
Example:
vector<int> numbers;
//Code to put values in my vector
for (vector<int>::iterator i = numbers.begin(); i < numbers.end(); i++)
cout << *i << '\n';
vs:
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';
Upvotes: 60
Views: 15683
Reputation: 43
If your compiler optimises your code, it doesn't matter which one you use. Otherwise, you might want to use the more OOP-looking format: numbers.begin()
This code has been plugged into Quick Bench for speed measurement:
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);
All compiled using STL=libstdc++ (GNU), results for begin()
are as follows:
params \ cpu_time (4dp) | formatA | formatB | Conclusion |
---|---|---|---|
optim = none compiler = Clang 11.0 std = c++11 |
2.1914 | 5.1870 | A is 2.4 times faster than B |
optim = none compiler = Clang 15.0 std = c++20 |
2.0666 | 2.8974 | A is 1.4 times faster than B |
optim = O3 compiler = Clang 11.0 std = c++11 |
1.1094 | 1.0130 | roughly equivalent runtime (equivalent assembly) |
optim = O3 compiler = Clang 15.0 std = c++20 |
1.0093 | 1.0007 | roughly equivalent runtime (equivalent assembly) |
Whereas for end()
:
params \ cpu_time (4dp) | formatA | formatB | Conclusion |
---|---|---|---|
optim = none compiler = Clang 11.0 std = c++11 |
2.5166 | 2.6341 | roughly equivalent runtime |
optim = none compiler = Clang 15.0 std = c++20 |
2.3657 | 3.8461 | A is 1.6 times faster than B |
optim = O3 compiler = Clang 11.0 std = c++11 |
1.0045 | 1.0126 | roughly equivalent runtime (equivalent assembly) |
optim = O3 compiler = Clang 15.0 std = c++20 |
1.0047 | 1.0012 | roughly equivalent runtime (equivalent assembly) |
Without optimisation,
formatA callq
s <std::vector<int, std::allocator<int> >::begin()>
and end()
respectively, while
formatB callq
s <decltype (({parm#1}.begin)()) std::begin<std::vector<int, std::allocator<int> > >(std::vector<int, std::allocator<int> >&)>
and end()
respectively also. Time will be lost by deducing data type for formatB.
WITH optimisation, the compiler has already done the deducing, both will be using the following assembly and varies nothing else other than addresses:
mov 0x3c5b1(%rip),%rax # 24f188 <arr>
mov %rax,0x8(%rsp)
add $0xffffffffffffffff,%rbx
Upvotes: 0
Reputation: 30876
The implementation of std::begin()
for vectors simply calls std::vector<T>::begin()
, so there is no difference between the two in this exact case.
Where std::begin()
comes into its own is in generic algorithms:
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 ...
}
Upvotes: 7
Reputation: 249384
std::begin()
was added in C++11 to make it easier to write generic code (e.g. in templates). The most obvious reason for it is that plain C-style arrays do not have methods, hence no .begin()
. So you can use std::begin()
with C-style arrays, as well as STL-style containers having their own begin()
and end()
.
If you're writing code which is not a template, you can ignore std::begin()
; your fellow programmers would probably find it odd if you suddenly started using it everywhere just because it's new.
Upvotes: 62