According to the draft standard (23.3.6.4 vector data), data() points to the underlying array and [data(), data() + size())
must be a valid range:
None of the wording in the standard suggests a given value for data() if the vector is empty().
And here's some definitive proof of why you should not assume it might be zero, even though it sometimes is:
#include <vector>
#include <iostream>
void value_of_data(std::vector<int> const& v)
{
std::cout << "empty() = " << v.empty() << ", " << "data() = " << static_cast<const void*>(v.data()) << std::endl;
}
int main()
{
std::vector<int> v;
value_of_data(v);
v.resize(100, 0);
v.clear();
value_of_data(v);
}
example output (gcc7.2, -O2, linux):
empty() = 1, data() = 0
empty() = 1, data() = 0x7ebc30
http://coliru.stacked-crooked.com/a/dd1d13200c8b9a3a
There is a state that an object can be valid but unspecified:
valid but unspecified state [§ 17.3]
an object state that is not specified except that the object’s invariants are met and operations on the object behave as specified for its type
[Example: If an object x of type std::vector is in a valid but unspecified state, x.empty() can be called unconditionally, and x.front() can be called only if x.empty() returns false. —end example]
Reading the C++ standard, the state of data()
is not specified when the vector is empty. So, the state is valid but unspecified state. Therefore, the returned value of data()
when the vector is empty can be anything (null or a random value). It depends on implementation of the compiler.
In this case, following the example in §17.3, you should call empty()
before using data()
to ensure that the returned value is as your expectation.
if (!v.empty())
do_something(v.data())
Yes, this is possible, and libstdc++ is doing this. You can have a look at the documentation of data() in libstdc++
data() _GLIBCXX_NOEXCEPT
{ return _M_data_ptr(this->_M_impl._M_start); }
However, performing actions on this pointer may not be specified, as you are accessing an non-initialized range within your vector, without its knowledge (e.g you don't know the exact size of the memory bunch). Also, as size()
is 0, your valid range is still 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.