我已经编写了一系列功能模板,可以尽可能轻松地将任意内容转换为文本。例如,
print(std::pair<int, int> {13, 1});
将打印{13, 1}
以及更长的内容,例如
std::vector<std::tuple<double, std::string>> vect;
for(int i=0;i<3;++i) {
double root = sqrt(i);
vect.push_back( {root, "sqrt " + std::to_string(i) } );
}
print(vect);
将输出:{ {0, "sqrt 0" }, {1, "sqrt 1"}, {1.41421, "sqrt 2"} }
我们说我有以下结构:
struct point { int x, y; };
编写类似下面的代码有多危险?
std::vector<point> my_points;
//Add points into my_points;
print(reinterpret_cast<const std::vector<std::pair<int, int>>&>(my_points));
它在gcc中编译并产生预期的输出,虽然我担心如果有人试图移植代码它可能会失败。
答案 0 :(得分:3)
首先,您没有在此处进行任何动态投射,因为您的代码中没有一次调用dynamic_cast
。你在这里做的是由reinterpet_cast
制作的c-cast
。
其次,不,它不安全,并且在许多层面上都是未定义的行为。
答案 1 :(得分:1)
非常危险。你有未定义的行为。 std::vector<std::pair<int, int>>
和std::vector<Point>
是完全,区域和分隔的类,彼此无关。将它们重新解释为未定义的行为。
事实上,标准中定义的案例恰好不适用。尝试将std::vector<char>
投射到std::vector<bool>
。即使char
和bool
具有相同的大小,但两个向量都不兼容。
如果您想避免复制缓冲区,请考虑使用模板:
template<typename T>
void print(const std::vector<T>& vec) {
// ...
}
更好的是,不要强迫矢量。如果您有std::array<Point, n>
,则可能需要使用您的功能:
template<typename T>
void print(const T& range) {
// ...
}
答案 2 :(得分:0)
您可以传递两个std::vector<T>
,一个const T*
和一个长度,或两个指针的结构,而不是传递const T*
。
现在你知道没有专门化(如std::vector<bool>
的情况,请参阅Why is vector<bool> not a STL container?),你也知道你有T:s的连续记忆。现在提供知道
Foo
s和Bar
s具有完全相同的布局(对齐,大小)Foo
和Bar
共享使用行为(如果有一个函数f(Foo)
,并且您在print
中使用该函数,还必须有一个语义相同的函数f(Bar)
)。更理论的表述如下:Foo
相当于Bar
相当于f
。然后,它应该是安全的,但你是你自己的,因为编译器无法真正检查(2)。例如,使用两个版本的Point(让我们使用float
代替int
):
struct PointXY{float x;float y;};
struct PointYX{float y;float x;};
float inner_product(PointXY p1,PointXY p2)
{return p1.x*p2.x + p1.y*p2.y;}
float inner_product_with_important_y(PointXY p1,PointXY p2)
{return 0.5f*( p1.x*p2.x + 4.0f*p1.y*p2.y );}
也就是说,您可以使用任意组合inner_product
和PointXY
安全地拨打PointYX
(同时满足(1)和(2)),但只要您调用各向异性版本,你会得到错误的结果((1)满足,但不是(2))。