Passing pointers to function taking iterators

Suppose for some reason this code is meant:
1
2
3
4
5
6
7
8
9
10
11
12
13
void accum(std::vector<double>::iterator bg, std::vector<double>::iterator end) 
{ std::cout << std::accumulate(bg, end, 0.0); }

int main()
{
   std::vector v {3.3,2.2,5.5};
   
  // std::vector<double>::iterator it {&v[0]}; 
  // std::cout << *it << '\n'; // possible
 
   double* first { &v[0]};
   accum(first, first+v.size()); // get error, why!?
}

error: could not convert 'first' from 'double*' to 'std::vector<double>::iterator'
Last edited on
...because the function explicitly wants an std::vector<double>::iterator, but you are passing a "raw" double* pointer.

Try instead:
1
2
std::vector v {3.3,2.2,5.5};
accum(v.begin(), v.end());


https://cplusplus.com/reference/vector/vector/begin/
https://cplusplus.com/reference/vector/vector/end/
Last edited on
frek wrote:
8
9
  // std::vector<double>::iterator it {&v[0]}; 
  // std::cout << *it << '\n'; // possible 

I don't think you're supposed to be able to initialize a std::vector<double>::iterator with a pointer like that. It compiles with libstdc++ (GCC's implementation of the standard library) but not in debug mode (i.e. when using -D_GLIBCXX_DEBUG). It also doesn't seem to work with libc++ (Clang's own implementation) nor with MSVC.
Last edited on
yeah, generally we shouldn't mess with using one of them instead of the other.
Of course things like ptr {&*iter} is possible.
A question about auto which has involved my mind for considerable time! :(
Would you use auto for functions parameters types if they're long and many ones (to shorten the function signature)?
Like void accum(auto bg, auto end); in the first post.

I assume it causes the signature to look shorter but also less readable making the code even error-prone potentially. As well as, auto as the parameter type should be used usually instead of a template not everywhere just to shorten the parameter types.
One remedy might be using using generally: using vecItr = std::vector<double>::iterator;
What are your thoughts and do you ever use auto for function parameters types just to make them shorter?
Last edited on
frek wrote:
Would you use auto for functions parameters types if they're long and many ones (to shorten the function signature)?

No, not only for that reason, because that turns it into a template.

In this case making it a template is probably a good idea though.

frek wrote:
Like void accum(auto bg, auto end); in the first post.

I would use the explicit template syntax:

1
2
3
4
5
template <typename Iter>
void accum(Iter begin, Iter end)
{
	...
}

Partially because that's what I'm used to but also because it forces both parameters to be the same type.

If you use auto it would have been equivalent to:

1
2
3
4
5
template <typename Iter1, typename Iter2>
void accum(Iter1 begin, Iter2 end)
{
	...
}

It probably doesn't make a lot of difference in this case because if you passed two different iterator types it would still fail to compile because std::accumulate doesn't accept iterators of different types but I still prefer the first one because it's nicer to get an error about the actual function I'm calling like:
error: no matching function for call to ‘accum(IterType1&, IterType2&)’
than to get an error about some implementation detail:
error: no matching function for call to 'accumulate(...
Last edited on
I agree with using explicit templates only if they're constrained, something which is harder when using auto.
If I consider not using templates in any form (explicit or auto), I will go for using aliases for such cases.
Last edited on
yeah, generally we shouldn't mess with using one of them instead of the other.
Of course things like ptr {&*iter} is possible.

The operator*() of the std::iterator gives you a reference to the "current" element. This then can be used to obtain a "raw" pointer to that element, by using the address-of (&) operator. So, yeah, you can get a "raw" pointer from an iterator.

But it is not possible the other way around. You can not simply wrap an arbitrary "raw" pointer into a specific type of iterator...
Last edited on
To get a raw pointer, you can also use std::addressof and std::to_address
https://en.cppreference.com/w/cpp/memory/to_address
https://en.cppreference.com/w/cpp/memory/addressof

but as mentioned above, AFAIK there's no equivalent to obtain an iterator.
Of course that iter shouldn't be end()! (UB)
Note that a pointer to a vector or array element can be used as an iterator because it fulfills all requirements for being an iterator (a random-access iterator to be precise).

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <vector>
#include <numeric>

int main()
{
	std::vector<int> vec = {3, 2, 5};
	
	int* begin = vec.data(); // same as &vec[0] if !vec.empty()
	int* end = vec.data() + vec.size();
	
	std::cout << std::accumulate(begin, end, 0); // prints 10
}

The above is perfectly valid and guaranteed to work.

I think an implementation is even allowed to define std::vector<T>::iterator as an alias for T* but none seems to do that. The situation for std::array and std::initializer_list is more interesting.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <iomanip>
#include <vector>
#include <array>
#include <initializer_list>

int main()
{
	std::cout << std::boolalpha;
	std::cout << std::is_same_v<std::vector<int>::iterator, int*> << "\n";
	std::cout << std::is_same_v<std::array<int, 5>::iterator, int*> << "\n";
	std::cout << std::is_same_v<std::initializer_list<int>::iterator, const int*> << "\n";
}

The above code prints
false
true
true
for me with GCC (libstdc++).

Note that I used const int* for the initializer_list because the elements of a initializer_list are essentially const.
Last edited on
Tested this using -std=c++23:
1
2
3
4
std::cout << std::boolalpha << std::same_as<std::vector<double>::iterator, double*> << ' '
		<< std::same_as<std::array<double, 5>::iterator, double*> << ' '
		<< std::convertible_to<double*, std::vector<double>::iterator> << ' '
		<< std::convertible_to<double*, std::array<double, 5>::iterator> << '\n';

MSVC:
false false false false
GCC:
false true false true
CLang:
false true false true
(cpp.sh, I assume it uses CLang compiler)

Why don't you, Peter, as a great coder, go for C++20/23. There're many things that can save your day when coding in them!
Last edited on
frek wrote:
Why don't you, Peter, as a great coder, go for C++20/23. There're many things that can save your day when coding in them!

I guess you mean I should have used std::same_as?
I don't know everything. :)
Looking at same_as on cppreference (https://en.cppreference.com/w/cpp/concepts/same_as) it's not even clear to me that it can be used as a bool.
Concepts is a feature I still have a lot to learn about.
The compiler I have installed on my computer only have partial C++20 support.
Last edited on
Topic archived. No new replies allowed.