2016-06-01 2 views
1

Я хотел бы знать, как я могу заменить содержимое вектора подмножеством этого вектора, не перераспределяя новый вектор (каждый вопрос, который я нашел, как представляется, имеет целью выделение нового вектора)Как разрезать вектор в C++ и присваивать самому себе?

пример:

vector<int>* testVec = new vector<int>{1, 2, 3, 4, 5}; 
//do some operation to slice vector, to say {2, 3, 4} without re-allocating 
//testVec should now have contents {2, 3, 4} 
+1

Взгляните на диапазоны и виды eric nieblers: https://github.com/ericniebler/range-v3 Возможно, вы захотите. – midor

+1

стирание первого и последнего элемента? – Slava

+5

Не используйте 'new', как это, ugh. :( – erip

ответ

5

вопрос не ясно, на сферу использования, но два вызова std::vector::erase хватит и не требует перераспределения.

Для примера:

std::vector<int> testVec{1, 2, 3, 4, 5}; 
testVec.erase(testVec.begin() + 4, testVec.end()); 
testVec.erase(testVec.begin(), testVec.begin() + 1); 

К первому удалению хвоста вектора вы можете убедиться в том, что значения только один раз перемещены.

Here is the full code.

Обратите внимание, что я предположил, что вам не нужно использовать указатель на std::vector.

+0

Стереть на e nd может быть достигнуто путем вызова 'resize' с аргументом меньше текущего размера. В этом случае 'resize (4)' –

+0

@Revolver_Ocelot, но это приведет к перераспределению и перемещению/копированию остальных элементов. ;-) – Hiura

+0

Нет, он не будет перераспределяться, поскольку это приведет к недействительности итераторов, и это может быть сделано только при изменении емкости (изменение размера было вызвано с аргументом больше текущей емкости). –

2

Вы можете использовать комбинацию std::rotate и vector::erase

void slice(vector<int> & v, std::size_t start, std::size_t size) 
{ 
    assert(v.size() > start + size); 
    std::rotate(v.begin(), v.begin() + start, v.end()); 
    // Vector is now {2,3,4,5,1} 
    v.erase(v.begin() + size, v.end()); 
} 
+0

В качестве альтернативы, после поворота вы можете просто использовать 'resize' для уменьшения размера. –

+0

С другой стороны, это не является эффективным решением. 'rotate' будет копировать/перемещать элементы, которые будут удалены на следующей строке. – sbabbi

2

Просто удалите части, которые вы не хотите:

std::vector<int> vec{1, 2, 3, 4, 5}; 
vec.erase(vec.begin() + 4, vec.end()); 
vec.erase(vec.begin(), vec.begin() + 1); 
3

Простой способ заключается в использовании подхода чтения/записи:

int wp = 0; // "write ptr" 
int rp = 0; // "read ptr" 
for (auto& x : v) { 
    if (... i want to keep element x, index rp ...) { 
     if (rp != wp) v[wp] = std::move(x); 
     wp++; 
    } 
    rp++; 
} 
v.resize(wp); 

такого рода схемы работает также для фильтрации на условиях которые зависят как от индекса, так и от значения элемента.

2

Это небольшая функция, которая использует предикат.

Для каждого элемента контейнера, который передает предикат, элемент стирается из контейнера.

Нет Перераспределение сделано, и каждый элемент std::move «d не более одного раза:

template<class C, class F> 
void erase_if(C& c, F&& f) { 
    using std::begin; using std::end; 
    auto it = std::remove(begin(c), end(c), std::forward<F>(f)); 
    c.erase(it, end(c)); 
} 

Делать это с индексами немного сложнее. Если индексы смежны, вы можете просто повернуть первый в первую позицию, затем удалить хвост контейнера или стереть хвост и фронт, или несколько мириад.

Если это не так, в основном вам нужно написать ручную версию std::remove, которая работает с индексами вместо значений элементов.

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

template<class Begin, class End, class Test> 
void remove_by_index(Begin b, End e, Test t) { 
    auto writer = b; 
    auto reader = b; 
    std::size_t index = 0; 
    while (reader != e) { 
    if (t(index)) { 
     ++reader; ++index; 
     continue; 
    } 
    if (reader != writer) { 
     *writer = std::move(*reader); 
    } 
    ++reader; ++writer; ++index; 
    continue; 
    } 
    return writer; 
} 

дает нам:

template<class C, class F> 
void erase_by_index(C& c, F&& f) { 
    using std::begin; using std::end; 
    auto it = remove_by_index(begin(c), end(c), std::forward<F>(f)); 
    c.erase(it, end(c)); 
} 

Предположим, что вы хотите сохранить срез всех четных позиций элементов:

erase_by_index(vec, [](auto i){return i&1;}); 

или предположим, что мы хотим сохранить Интервал:

template<class C> 
void keep_interval(C& c, std::size_t start_index, std::size_t length) { 
    erase_by_index(c, [=](auto i){ return i < start_index || i >= (start_index+length); }); 
} 

Теперь более общий способ состоит в создании альтернативного вида вашего контейнера, где каждый элемент в представлении соответствует некоторому диапазону элементов в исходном контейнере, выполняя тесты на этом представлении, а затем применяя операцию на оригинальный контейнер.

Не знаете, как писать это лаконично.

 Смежные вопросы

  • Нет связанных вопросов^_^