In std::iterator_traits, what is the difference between '::value_type *' and ::pointer?

95 Views Asked by At

I've read this from Cppreference
I'm confusing about what is the difference between:

  • std::iterator_traits<it>::value_type *
  • and std::iterator_traits<it>::pointer.

I did dive in the text and try to figure out the difference, but the more I read the more I get confusing.
Something tells me they would be the same, but logically they seem not.
So what is the actual difference between them, I would like an example if possible.

2

There are 2 best solutions below

5
HolyBlackCat On BEST ANSWER

value_type is supposed to have cv-qualifiers (const and/or volatile) removed from it, so you could have e.g.:

using value_type = int;
using pointer = const int *;
using reference = const int &;

Also, for the quirky iterators that compute the result of dereferencing on the fly (such as std::vector<bool>::iterator), reference (the return type of operator*) and pointer (the return type of operator->) can be proxy classes that merely pretend to be pointers/references.

The exact requirements on those proxy classes appear to be underspecified, but at least reference is expected to be convertible to value_type, and pointer is expected to overload -> and work with std::to_address.

2
user17732522 On

They are usually the same in most cases that you will come across, except that a const iterator will have const value_type* for pointer and that sometimes *pointer is a proxy type rather than value_type itself (see other answers).

But also, the standard's definition for an iterator does not require that pointer is actually a raw native pointer. Instead it is also somewhat common to be a type that behaves like a raw native pointer. Such types are called "fancy pointers".

You can find a short explanation of fancy pointers here on cppreference.

One example is boost::interprocess::offset_ptr which is a fancy pointer type that is used to address objects in memory shared between multiple processes. Because the shared memory is mapped to different base addresses in the different processes, you can't use a normal raw pointer. Instead you can only store the offset to a common reference point in memory (i.e. the offset pointer's location in the shared memory itself), so that all processes can agree where the offset pointer points. If you have e.g. a container implemented for shared memory using this offset pointer, then std::iterator_traits<it>::pointer would be that offset_ptr<value_type>, not value_type*, for iterators into that container.

Formally, the only purpose/requirement of pointer for an iterator is that it determines the result type of operator-> or void if there is no operator->.

The only exception are contiguous iterators for which it must be possible to convert pointer to a native pointer and back through to_address and to_pointer since this mapping is used to define contiguity.