What Should Std::Vector::Data() Return If the Vector Is Empty

What should std::vector::data() return if the vector is empty?

The convention for ranges is [inclusive, exclusive), that is if you iterate over a range [X,Y) you will conceptually do the following (pseudo-code):

for( iterator ii = X; ii != Y; ++ii) {
...
}

This permits to express an empty range as [X,X). Also this empty range is perfectly well defined for each address, no matter if it is valid or invalid.

That said the requirements for data() are (emphasis mine):

23.3.6.4 [vector.data]

T* data() noexcept;

const T* data() const noexcept;

Returns: A pointer such that [data(),data() + size()) is a valid range. For a
non-empty vector, data() == &front().

It seems to me that the only unconditional guarantee is that [data(),data() + size()) should be a valid range. For size() == 0 the member function data() may return any value and the range will be a valid empty range. Therefore I would say that an implementation is allowed to return a non-null pointer if size() is zero.

What does vec.data() return if vec.size() == 0?

Too long for a comment so posting here.

I expected the iterators to be nullptr for an empty sequence so I tested it.

#include <iostream>
#include <vector>

void pr(std::vector<int>& v){
std::cout << &*v.begin() << ", " << &*v.end() << "\n";
}
// technically UB, but for this experiment I don't feel too bad about it.
// Thanks @Revolver
int main(int argc, char** argv) {
std::vector<int> v1;
std::vector<int> v2;

pr(v1);
pr(v2);

return 0;
}

And this does indeed print

0, 0
0, 0

Now for an empty container the only reasonable operation for a valid range is begin() == end(). And no, junk can't be dereferenced so *v.begin() is not a concern.

std vector.data return void

vector<bool> is not a proper vector. As weird as that sounds, that's the way it is. It can't give you a pointer to its internal bool array, because it doesn't have one, since it stores the values packed into single bits.

What iterator does vector::erase() return if passed an empty range?

This is specified in [sequence.reqmts]:

The iterator returned by a.erase(q1, q2) points to the element pointed to by q2 prior to any elements being erased. If no such element exists, a.end() is returned.

(Note: I linked the C++17 final working draft, but this wording exists since at least C++98, see comment by @Peter)

So we should have it1 == v.begin() and it2 == v.end().

Live test:

#include <iostream>
#include <vector>

int main()
{
std::vector<int> v{1, 2, 3, 4};
auto it1 = v.erase(v.begin(), v.begin());
auto it2 = v.erase(v.end(), v.end());
std::cout << std::distance(v.begin(), it1) << std::endl;
std::cout << std::distance(v.begin(), it2) << std::endl;
}

Output:

0
4

To clarify this behavior, I have updated the cppreference documentation, which is now:

iterator erase( const_iterator pos );
iterator erase( const_iterator first, const_iterator last );

Return Value

Iterator following the last removed element.

If pos refers to the last element, then the end() iterator is returned.

If last==end() prior to removal, then the updated end() iterator is returned.

If [first, last) is an empty range, then last is returned.

Difference between std::vector::empty and std::empty

Difference between std::vector::empty and std::empty

The difference between Container::empty member function and std::empty free function (template) is the same as difference between Container::size,std::size, Container::data,std::data, Container::begin,std::begin and Container::end,std::end.

In all of these cases for any standard container, the free function (such as std::empty) simply calls the corresponding member function. The purpose for the existence of the free function is to provide a uniform interface between containers (and also std::initializer_list) and arrays. Arrays cannot have member functions like the class templates can have, so they have specialised overload for these free functions.

If you are writing code with a templated container type, then you should be using the free function in order to be able to support array as the templated type. If the type isn't templated, then there is no difference between the choice of using member function or the free function other than the convenience of potential refactoring into a template (or just plain array).



Related Topics



Leave a reply



Submit