C ++ STL Векторный эквивалент set_len () в Rust - PullRequest
2 голосов
/ 26 апреля 2020

TL; DR:

Как изменить размер (настроить length и capacity of) вектора C ++ STL без какой-либо инициализации ? Значения мусора приемлемы!

Вопрос

Мне известно, что в STL Vector есть метод resize(), но этот метод включает инициализацию , что может быть ненужным.

Более того, я нашел функцию set_len() в Rust, которая делает то, что я хочу именно. Есть ли способ (даже хакерский) для C ++ STL добиться этого?

set_len() do c в Rust можно найти здесь .

РЕДАКТИРОВАТЬ 1

  1. Я знаю, что установка длины, превышающей емкость вектора, равна неопределенное поведение , и я должен быть очень осторожным (unsafe fn, достаточно точно ), но я говорю о тех случаях, когда the_new_length_i_am_setting <= vec.capacity() равен ГАРАНТИРОВАН (у меня уже reserved() правильно).

  2. Меня не волнует, что значения будут заполнены этими дополнительными пробелами (мусор допустим), так как позже я буду перезаписывать их вручную. Разница между malloc() и calloc() является идеальной аналогией того, о чем я говорю.

  3. Мой вариант использования: хранить байты из нескольких read() s или recv() s вызывает один и тот же вектор напрямую, без использования дополнительного массива / буфера. (Я добился этого в Ve c Rust, используя reserve(), а затем set_len(), но мне не удалось найти эквивалентную функцию для set_len() в C ++ STL vector.

  4. Чтобы упростить понимание, я в основном пытаюсь использовать vector в Linux системных API-интерфейсах, которые принимаются только для массивов. Вызов realloc() для malloc() -объектного массива определенно сделает то же самое, но это подвержен ошибкам, особенно при работе с индексами.

1 Ответ

4 голосов
/ 27 апреля 2020

Предисловие (это может быть длинным, но очень важным)

Вы можете сказать, что «значения мусора» приемлемы, но это не так. Потому что, когда люди говорят, что в их коде C ++ есть значения мусора, которых они на самом деле не имеют, они фактически имеют неопределенное поведение. И вы не должны воспринимать слегка неопределенное поведение. Позвольте мне процитировать из другого моего ответа :

void foo();
void bar();

void test(bool cond)
{
    int a; // uninitialized

    if (cond)
        a = 24;

    if (a == 24)
        foo();
    else
        bar();
}

Каков результат вызова вышеуказанной функции с true? Как насчет false?

test(true), будет очищен вызов foo().

Как насчет test(false)? Если вы ответите: «Ну, это зависит от того, что значение мусора находится в переменной a, если это 24, это вызовет foo, иначе это вызовет bar» Тогда вы полностью неправильно.

Если вы вызываете test(false), программа обращается к неинициализированной переменной и имеет неопределенное поведение, это недопустимый путь, и поэтому компиляторы могут предполагать, что cond никогда не будет false (потому что в противном случае программа была бы нелегальной). И неожиданный сюрприз, и g cc, и clang с включенными оптимизациями фактически делают это, и генерирует эту сборку для функции:

test(bool):
        jmp     foo()

Мораль этой истории заключается в том, что UB - это UB. Не полагайтесь на какое-либо поведение. Доступ к неинициализированным переменным или памяти не приводит к значениям мусора , это приводит к UB, и результаты могут быть очень плохими, чрезвычайно удивительными и трудными для отладки.


Возвращаясь к Ваш вопрос: нет, в std::vector нет способа выделить неинициализированную память, к которой у вас есть доступ. Т.е. это UB, и вы на 100% не должны этого делать:

std::vector<int> v = ...;

v.reserve(v.size() + 100);

v[v.size()] = 11; // Access beyond vector's size,
                  // Undefined Behavior even if you reserved memory for it

Я знаю, что установка длины, превышающей емкость вектора, является неопределенным поведением

Нет, это не так:

std :: vector :: resize

void resize( size_type count );

Изменяет размер контейнера, чтобы в нем содержалось количество элементов.

Если текущий размер меньше количества, добавляются дополнительные вставленные по умолчанию элементы

Конечно, недостатком является то, что элементы будут вставлены по умолчанию. Но, как я уже сказал, в std:::vector


обойти это невозможно. Теоретически невозможно сделать даже без std::vector. Существует длительная проблема с правилами жизни объектов в C ++ (даже несмотря на то, что де-факто это игнорируется на практике). Есть бумага p0593r2 , которая пытается решить эту проблему. Но даже с решением, принятым в стандарте, вам все равно придется реализовать свой собственный контейнер, чтобы разрешать и делать то, что вы хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...