Что должно возвращать std::vector:: data(), если вектор пуст?
В соответствии с проектом стандарта (23.3.6.4 векторные данные), данные() указывают на базовый массив и [data(), data() + size())
должны быть допустимый диапазон:
T* data() noexcept;
const T* data() const noexcept;
1 Returns: A pointer such that [data(),data() + size()) is a valid range. For a non-empty vector,
data() == &front().
2 Complexity: Constant time
Но что, если вектор пуст? Когда я создаю вектор нулевого размера:
#include <vector>
#include <iostream>
int main() {
const int NUM = 0*10;
std::vector< double > v( NUM, 0.0 );
std::cerr << "V : "<< v.data() << std::endl;
}
MSVC 2010 возвращает null, но в Linux (с GCC 4.2.1 и Intel 12.1) я получаю ненулевой адрес.
Может ли vector::data()
разрешить или вернуть значение null? Может ли реализация, например, выполнить первоначальное распределение по умолчанию и вернуть указатель (не нуль)?
Изменить: В нескольких ответах основное внимание уделяется действительности пустого диапазона. Я полностью согласен с этим.
Мне бы очень хотелось увидеть хорошую ссылку или пояснение: разрешено ли ей возвращать null или он может также возвращать непустые?
Ответы
Ответ 1
Соглашение для диапазонов [inclusive, exclusive)
, то есть, если вы перебираете диапазон [X,Y)
, вы концептуально выполните следующий (псевдокод):
for( iterator ii = X; ii != Y; ++ii) {
...
}
Это позволяет выразить пустой диапазон как [X,X)
. Также этот пустой диапазон отлично определен для каждого адреса, независимо от того, действителен он или недействителен.
Тем не менее, требования для data()
(выделено мной):
23.3.6.4 [vector.data]
T * data() noexcept;
const T * data() const noexcept;
Возвращает: указатель такой, что [data(), data() + size()) является допустимым диапазоном. Для непустой вектор, data() == & front().
Мне кажется, что единственная безусловная гарантия заключается в том, что [data(),data() + size())
должен быть допустимым диапазоном. Для size() == 0
функция-член data()
может возвращать любое значение, и диапазон будет действительным пустым диапазоном. Поэтому я бы сказал, что реализации разрешено возвращать ненулевой указатель, если size()
равно нулю.
Ответ 2
Ни одна из формулировок в стандарте не предлагает заданное значение для данных(), если вектор пуст().
И вот какое-то окончательное доказательство того, почему вы не должны предполагать, что это может быть ноль, хотя иногда это:
#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);
}
Пример вывода (gcc7.2, -O2, linux):
empty() = 1, data() = 0
empty() = 1, data() = 0x7ebc30
http://coliru.stacked-crooked.com/a/dd1d13200c8b9a3a
Ответ 3
Существует состояние, в котором объект может быть действительным, но неопределенным:
действительное, но неопределенное состояние [§ 17.3]
состояние объекта, которое не указано, за исключением того, что объекты инварианты выполняются, и операции над объектом ведут себя как указано для его типа
[Пример: если объект x типа std::vector находится в действительном, но неуказанное состояние, x.empty() можно назвать безоговорочно, и x.front() может вызываться только в том случае, если x.empty() возвращает false. -end пример]
Считывая стандарт С++, состояние data()
не указывается, когда вектор пуст. Таким образом, состояние является действительным, но неопределенным. Поэтому возвращаемое значение data()
, когда вектор пуст, может быть любым (нулевое или случайное значение). Это зависит от реализации компилятора.
В этом случае, следуя примеру в § 17.3, вы должны вызвать empty()
перед тем, как использовать data()
, чтобы гарантировать, что возвращаемое значение будет вашим ожиданием.
if (!v.empty())
do_something(v.data())
Ответ 4
Да, это возможно, и libstdС++ делает это.
Вы можете посмотреть документацию data() в libstdС++
data() _GLIBCXX_NOEXCEPT
{ return _M_data_ptr(this->_M_impl._M_start); }
Однако выполнение действий над этим указателем может не указываться, поскольку вы получаете доступ к неинициализированному диапазону внутри вашего вектора, без его ведома (например, вы не знаете точный размер пакета памяти). Кроме того, поскольку size()
равно 0, ваш допустимый диапазон все еще пуст.