Подтвердить что ты не робот

Что должно возвращать 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 или он может также возвращать непустые?

4b9b3361

Ответ 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, ваш допустимый диапазон все еще пуст.