【发布时间】:2018-04-07 06:01:18
【问题描述】:
短版
我可以将reinterpret_caststd::vector<void*>* 转为std::vector<double*>*吗?
其他 STL 容器呢?
加长版
我有一个函数可以将 void 指针向量重新转换为模板参数指定的数据类型:
template <typename T>
std::vector<T*> recastPtrs(std::vector<void*> const& x) {
std::vector<T*> y(x.size());
std::transform(x.begin(), x.end(), y.begin(),
[](void *a) { return static_cast<T*>(a); } );
return y;
}
但我认为复制矢量内容并不是真正必要的,因为我们实际上只是重新解释所指向的内容。
经过一番修改,我想出了这个:
template <typename T>
std::vector<T*> recastPtrs(std::vector<void*>&& x) {
auto xPtr = reinterpret_cast<std::vector<T*>*>(&x);
return std::vector<T*>(std::move(*xPtr));
}
所以我的问题是:
- 像这样 reinterpret_cast 整个向量是否安全?
- 如果它是不同类型的容器(如
std::list或std::map)怎么办?明确地说,我的意思是将std::list<void*>转换为std::list<T*>,而不是在 STL 容器类型之间进行转换。 - 我仍在尝试围绕移动语义进行思考。我做得对吗?
还有一个后续问题:在不重复代码的情况下生成const 版本的最佳方法是什么?即定义
std::vector<T const*> recastPtrs(std::vector<void const*> const&);
std::vector<T const*> recastPtrs(std::vector<void const*>&&);
MWE
#include <vector>
#include <algorithm>
#include <iostream>
template <typename T>
std::vector<T*> recastPtrs(std::vector<void*> const& x) {
std::vector<T*> y(x.size());
std::transform(x.begin(), x.end(), y.begin(),
[](void *a) { return static_cast<T*>(a); } );
return y;
}
template <typename T>
std::vector<T*> recastPtrs(std::vector<void*>&& x) {
auto xPtr = reinterpret_cast<std::vector<T*>*>(&x);
return std::vector<T*>(std::move(*xPtr));
}
template <typename T>
void printVectorAddr(std::vector<T> const& vec) {
std::cout<<" vector object at "<<&vec<<", data()="<<vec.data()<<std::endl;
}
int main(void) {
std::cout<<"Original void pointers"<<std::endl;
std::vector<void*> voidPtrs(100);
printVectorAddr(voidPtrs);
std::cout<<"Elementwise static_cast"<<std::endl;
auto dblPtrs = recastPtrs<double>(voidPtrs);
printVectorAddr(dblPtrs);
std::cout<<"reintepret_cast entire vector, then move ctor"<<std::endl;
auto dblPtrs2 = recastPtrs<double>(std::move(voidPtrs));
printVectorAddr(dblPtrs2);
}
示例输出:
Original void pointers
vector object at 0x7ffe230b1cb0, data()=0x21de030
Elementwise static_cast
vector object at 0x7ffe230b1cd0, data()=0x21de360
reintepret_cast entire vector, then move ctor
vector object at 0x7ffe230b1cf0, data()=0x21de030
注意reinterpret_cast 版本重用了底层数据结构。
以前提出的似乎不相关的问题
这些是我尝试搜索时出现的问题:
reinterpret_cast vector of class A to vector of class B
reinterpret_cast vector of derived class to vector of base class
reinterpret_cast-ing vector of one type to a vector of another type which is of the same type
参考严格的别名规则,这些问题的答案是一致的“否”。但我认为这不适用于我的情况,因为被重铸的向量是一个右值,所以没有混叠的机会。
我为什么要这样做
我正在与一个 MATLAB 库进行交互,该库为我提供void* 形式的数据指针以及一个指示数据类型的变量。我有一个函数可以验证输入并将这些指针收集到一个向量中:
void parseInputs(int argc, mxArray* inputs[], std::vector<void*> &dataPtrs, mxClassID &numericType);
我无法对这部分进行模板化,因为直到运行时才知道类型。另一方面,我有数字例程来对已知数据类型的向量进行操作:
template <typename T>
void processData(std::vector<T*> const& dataPtrs);
所以我只是想把一个连接到另一个:
void processData(std::vector<void*>&& voidPtrs, mxClassID numericType) {
switch (numericType) {
case mxDOUBLE_CLASS:
processData(recastPtrs<double>(std::move(voidPtrs)));
break;
case mxSINGLE_CLASS:
processData(recastPtrs<float>(std::move(voidPtrs)));
break;
default:
assert(0 && "Unsupported datatype");
break;
}
}
【问题讨论】:
-
processData<float>仍然可以接受vector<void *>并在需要的时候将void *隐式转换为float *。还可以考虑对processData使用开始-结束范围而不是向量(尽管这不能解决问题)
标签: c++ c++11 casting stl move-semantics