3

I want my function to be able to take array.begin() and array.end() as arguments. As far as I understand, the begin/end functions return a pointer to the first/last element of the array. Then why does the following code not work? How should I write the code instead?

#include <iostream>
#include <array>

template<class T> void foo(const T* begin, const T* end) {
     ...
}

int main() {
     std::array<int, 5> arr = { 1, 2, 3, 4, 5 };
     foo(arr.begin(), arr.end());          // <-- error!

     return 0;
}
4
  • 6
    Note that in general begin/end return iterators which behave like pointers (and may even be implemented as pointers), but are not necessarily pointers themselves. Your code woks fine in GCC and Clang, since their standard library implementations both implement std::array's iterators as pointers, but it doesn't work under MSVC, which defines a separate iterator class for std::array. Commented Dec 10, 2021 at 23:23
  • 4
    begin and end return iterators which in some standard libraries might just be a pointer but there is no guarantee Commented Dec 10, 2021 at 23:23
  • 1
    Welcome to Stack Overflow. "Then why does the following code not work" Because your understanding is incorrect. "and how to solve my problem the right way?" The first step is to look up the documentation for the .begin and .end methods, which will show you what they actually return. Commented Dec 10, 2021 at 23:27
  • For future reference, please read meta.stackoverflow.com/questions/261592 and meta.stackoverflow.com/questions/343721; keep in mind that this is not a discussion forum and thus your experience level isn't relevant to the question. We strive for kindness anyway, but also for directness. Commented Dec 10, 2021 at 23:33

2 Answers 2

5

As far as I understand, the begin/end functions return a pointer to the first/last element of the array

No. begin and end return iterators. The standard library works with iterators. Iterators are a generalization of pointers.

Iterators behave like pointers and you use them like pointers (e.g. *it to access the element), with some caveats: not all iterators have all the operations a pointer does. A pointer satisfies the random access iterator concept, so on some implementations the iterator of std::array could be just an alias for the pointer type, but you can't rely on that. E.g. on the same compiler it can be a pointer for the release build, but a full class for the debug build.

The idiomatic way is to write:

template<class It>
void foo(It begin, It end) {
     for (auto it = begin; it != end; ++it) {
          const auto& elem = *it;
          // ..
     }
}

Since C++20 we should transition from iterator pairs to ranges:

void foo(std::ranges::range const auto& r) {
    for (const auto& elem : r) { 
        // ...
    }
}
Sign up to request clarification or add additional context in comments.

Comments

3

As far as I understand, the begin/end functions return a pointer

Your understanding is (generally) wrong. begin and end functions return std::array::iterator, as per documentation. That type is not necessarily a pointer.

to the first/last element of the array

end isn't an iterator to the last element, but one past the last element.

how to solve my problem the right way?

Accept arguments of type std::array::iterator.

That said, given that foo is a template, there seems to be little reason to not allow the argument to be of any iterator type:

template<class It> 
void
foo(It begin, It end)

Alternatively, you could accept a range:

template<class Range> 
void
foo(const Range& range)

// usage
foo(arr);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.