将std :: vector从一种数据类型转换为另一种数据类型(以节省空间的想法)的最快方法(如果有的话)是什么?例如:
std::vector<unsigned short> ----> std::vector<bool>
我们显然假设第一个向量只包含0和1。在矢量非常大的情况下,逐个元素复制是非常低效的。
条件问题: 如果您认为没有办法更快地完成它,是否有一个复杂的数据类型实际上允许从一种数据类型快速转换到另一种数据类型?
答案 0 :(得分:25)
std::vector<bool>
停止。
std::vector<bool>
是......不是。 std::vector
具有使用bool
类型的专门化,这会导致vector
中的某些更改。也就是说,它停止表现得像std::vector
。
标准保证您可以使用std::vector
执行某些操作。 vector<bool>
违反了这些保证。所以你应该非常小心使用它们。
无论如何,我打算假装你说vector<int>
而不是vector<bool>
,因为后者确实让事情变得复杂。
在矢量非常大的情况下,逐个元素复制是非常低效的。
只有你做错了。
您想要的类型的矢量转换需要仔细进行才能有效。
如果源T
类型可转换为目标T
,那么这样就可以了:
vector<Tnew> vec_new(vec_old.begin(), vec_old.end());
体面的实现应该识别它们何时被赋予随机访问迭代器并优化内存分配并适当地循环。
对于简单类型,不可转换类型的最大问题不是这样做:
std::vector<int> newVec(oldVec.size());
那很糟糕。这将分配一个适当大小的缓冲区,但它也用数据填充它。即,默认构造的int
s(int()
)。
相反,你应该这样做:
std::vector<int> newVec;
newVec.reserve(oldVec.size());
此容量等于原始向量,但它也确保不会发生默认构造。您现在可以push_back
了解您的内容,因为您知道您永远不会在新的向量中重新分配。
从那里,您可以循环遍历旧向量中的每个条目,根据需要进行转换。
答案 1 :(得分:15)
没有办法避免副本,因为std::vector<T>
是一个独特的
从std::vector<U>
输入,他们无法分享
记忆。除此之外,它取决于数据的映射方式。如果
映射对应于隐式转换(例如unsigned short
bool
),然后使用begin和end创建一个新的向量
来自旧的迭代器将起到作用:
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() );
最后,另一种可能性是使用a
boost::transform_iterator
。类似的东西:
std::vector<TargetType> newV(
boost::make_transform_iterator( oldV.begin(), TranformationObject() ),
boost::make_transform_iterator( oldV.end(), TranformationObject() ) );
在很多方面,这是我更喜欢的解决方案;取决于如何
boost::transform_iterator
已经实施,它也可能是
最快的。
答案 2 :(得分:9)
你应该可以像这样使用assign
:
vector<unsigned short> v;
//...
vector<bool> u;
//...
u.assign(v.begin(), v.end());
答案 3 :(得分:2)
最快的方法是不这样做。例如,如果您事先知道您的项目只需要一个字节进行存储,那么请先使用字节大小的向量。你会发现很难找到比这更快的方法: - )
如果那是不可能的,那么只需要承担转换成本。即使它有点慢(并且这不是确定的,请参阅Nicol's excellent answer了解详细信息),它仍然是必要的。如果不是,你只需将它留在较大类型的矢量中。
答案 4 :(得分:2)
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);
}
答案 5 :(得分:1)
首先,警告:不要做我要建议的事情。这很危险,绝不可能。也就是说,如果你只需要挤出一点点性能,那么无关紧要......
首先,有一些警告。如果你不满足这些,你就不能这样做:
向量必须包含普通旧数据。如果你的类型有指针,或者使用析构函数,或者需要operator =正确复制......不要这样做。
sizeof()两个向量包含的类型必须相同。即,矢量&lt; A&gt;可以从矢量&lt; 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 C ++检查过,它甚至没有关闭)。
另外,考虑到这些约束,你可以强行(通过void *)将一个矢量类型转换为另一个并交换它们 - 我有一个代码示例,但它开始在我的屏幕上渗出外质,所以我删除了它
答案 6 :(得分:0)
逐个元素复制效率不高。 std :: vector为其任何元素提供持续访问时间,因此整个操作将为O(n)。你不会注意到它。
答案 7 :(得分:0)
#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