Самый быстрый способ преобразования std::vector в другой std::vector

Каков самый быстрый способ (если есть какой-либо другой) преобразовать std::vector из одного типа данных в другой (с идеей сэкономить место)? Например:

std::vector<unsigned short> ----> std::vector<bool> 

мы, очевидно, предположим, что первый вектор содержит только 0s и 1s. Копирование элемента по элементу крайне неэффективно в случае действительно большого вектора.

Условный вопрос: Если вы считаете, что нет возможности сделать это быстрее, существует ли сложный тип данных, который фактически позволяет быстро преобразовать один тип данных в другой?

Ответ 1

std::vector<bool> 

Стоп.

A std::vector<bool> - это... нет. std::vector имеет специализацию для использования типа bool, что вызывает определенные изменения в vector. А именно, он перестает действовать как a std::vector.

Есть определенные вещи, которые стандарт гарантирует, что вы можете сделать с std::vector. И vector<bool> нарушает эти гарантии. Поэтому вы должны быть очень осторожны в их использовании.

В любом случае, я собираюсь притвориться, что вы сказали vector<int> вместо vector<bool>, поскольку последнее действительно усложняет ситуацию.

Копирование элемента по элементу крайне неэффективно в случае действительно большого вектора.

Только если вы ошибетесь.

Для того, чтобы быть эффективными, нужно сделать векторное литье того типа, который вы хотите. Самая большая проблема, которую вы будете иметь для простых типов, не делает этого:

std::vector<int> newVec(oldVec.size());

Это плохо. Это будет выделять буфер соответствующего размера, но он также заполнит его данными. А именно, построенный по умолчанию int (int()).

Вместо этого вы должны сделать следующее:

std::vector<int> newVec;
newVec.reserve(oldVec.size());

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

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

Ответ 2

Нет способа избежать копирования, так как a std::vector<T> является отдельным типа от std::vector<U>, и нет возможности для их совместного использования Память. Помимо этого, это зависит от того, как данные сопоставляются. Если сопоставление соответствует неявному преобразованию (например, unsigned short to bool), затем просто создавая новый вектор, используя начало и конец итераторы из старого будут делать трюк:

std::vector<bool> newV( oldV.begin(), oldV.end() );

Если отображение не просто неявное преобразование (и это включает случаи, когда вы хотите проверить вещи; например что unsigned short содержит только 0 или 1), тогда он становится более сложным. очевидным решением было бы использовать std:: transform:

std::vector<TargetType> newV;
newV.reserve( oldV.size() );    //  avoids unnecessary reallocations
std::transform( oldV.begin(), oldV.end(),
                std::back_inserter( newV ),
                TranformationObject() );

где TranformationObject - функциональный объект, который выполняет преобразование, например:

struct ToBool : public std::unary_function<unsigned short, bool>
{
    bool operator()( unsigned short original ) const
    {
        if ( original != 0 && original != 1 )
            throw Something();
        return original != 0;
    }
};

(Обратите внимание, что я просто использую эту функцию преобразования в качестве примера. Если единственное, что отличает функцию преобразования от неявное преобразование - это проверка, возможно, быстрее проверить все значения в oldV сначала, используя std::for_each, а затем используйте два конструктора итератора выше.)

В зависимости от стоимости по умолчанию для построения целевого типа это может быть быстрее создать новый вектор с правильным размером, а затем перезаписать это:

std::vector<TargetType> newV( oldV.size() );
std::transform( oldV.begin(), oldV.end(),
                newV.begin(),
                TranformationObject() );

Наконец, еще одна возможность - использовать boost::transform_iterator. Что-то вроде:

std::vector<TargetType> newV(
    boost::make_transform_iterator( oldV.begin(), TranformationObject() ),
    boost::make_transform_iterator( oldV.end(), TranformationObject() ) );

Во многом это решение, которое я предпочитаю; в зависимости от того, как boost::transform_iterator, он также может быть быстрее всего.

Ответ 3

Вы можете использовать assign следующим образом:

vector<unsigned short> v;
//...
vector<bool> u;
//...
u.assign(v.begin(), v.end());

Ответ 4

Самый быстрый способ сделать это - не делать этого. Например, если вы заранее знаете, что вашим элементам нужен только байт для хранения, сначала используйте вектор размера байта. Вам будет сложно найти более быстрый способ: -)

Если это невозможно, то просто поглотите стоимость конверсии. Даже если он немного медленный (и это отнюдь не обязательно, см. Nicol отличный ответ для подробностей), это все еще необходимо. Если это не так, вы просто оставите его в векторе большего типа.

Ответ 5

class A{... }
class B{....}
B convert_A_to_B(const A& a){.......}

void convertVector_A_to_B(const vector<A>& va, vector<B>& vb)
{
    vb.clear();
    vb.reserve(va.size());
    std::transform(va.begin(), va.end(), std::back_inserter(vb), convert_A_to_B);
}

Ответ 6

Во-первых, предупреждение: не делайте то, что я собираюсь предложить. Это опасно и никогда не должно быть сделано. Тем не менее, если вам просто нужно выжать немного больше производительности No Matter What...

Во-первых, есть некоторые предостережения. Если вы не встретите их, вы не сможете этого сделать:

  • Вектор должен содержать простые старые данные. Если ваш тип имеет указатели или использует деструктор или нужен оператор = для правильной копии... не делайте этого.

  • Размерные() векторные типы должны быть одинаковыми. То есть, вектор <A> может копировать из вектора <B> только если sizeof (A) == sizeof (B).

Вот довольно стабильный метод:

vector< A > a;
vector< B > b;
a.resize( b.size() );
assert( sizeof(vector< A >::value_type) == sizeof(vector< B >::value_type) );
if( b.size() == 0 )
   a.clear();
else
   memcpy( &(*a.begin()), &(*b.begin()), b.size() * sizeof(B) );

Это делает очень быструю, блочную копию памяти, содержащуюся в векторе b, непосредственно разбивая любые данные, которые у вас есть в векторе a. Он не вызывает конструкторы, он не выполняет никаких проверок безопасности, и он намного быстрее, чем любые другие методы, приведенные здесь. Оптимизирующий компилятор должен иметь возможность сопоставить скорость этого в теории, но если вы не используете необычно хороший, он не будет (я проверил с Visual С++ несколько лет назад, и он даже не был близок).

Кроме того, с учетом этих ограничений вы можете принудительно (через void *) передать один векторный тип другому и обменять их - у меня был образец кода для этого, но он начал просачивать эктоплазму на моем экране, поэтому я удалил его.

Ответ 7

Копирование элемента по элементу не очень неэффективно. std::vector обеспечивает постоянное время доступа к любому из его элементов, поэтому операция будет O (n) в целом. Вы не заметите этого.

Ответ 8

#ifdef VECTOR_H_TYPE1
#ifdef VECTOR_H_TYPE2
#ifdef VECTOR_H_CLASS
/* Other methods can be added as needed, provided they likewise carry out the same operations on both */

#include <vector>

using namespace std;

class VECTOR_H_CLASS {
public:
        vector<VECTOR_H_TYPE1> *firstVec;
        vector<VECTOR_H_TYPE2> *secondVec;

        VECTOR_H_CLASS(vector<VECTOR_H_TYPE1> &v1, vector<VECTOR_H_TYPE2> &v2) { firstVec = &v1; secondVec = &v2; }
        ~VECTOR_H_CLASS() {}

        void init() { // Use this to copy a full vector into an empty (or garbage) vector to equalize them
                secondVec->clear();
                for(vector<VECTOR_H_TYPE1>::iterator it = firstVec->begin(); it != firstVec->end(); it++) secondVec->push_back((VECTOR_H_TYPE2)*it);
        }

        void push_back(void *value) {
                firstVec->push_back((VECTOR_H_TYPE1)value);
                secondVec->push_back((VECTOR_H_TYPE2)value);
        }

        void pop_back() {
                firstVec->pop_back();
                secondVec->pop_back();
        }

        void clear() {
                firstVec->clear();
                secondVec->clear();
        }
};
#undef VECTOR_H_CLASS
#endif
#undef VECTOR_H_TYPE2
#endif
#undef VECTOR_H_TYPE1
#endif