我有一些 C++ 代码显示错误,但我无法理解为什么。 不过,我设法将代码简化为一个小示例。
我使用的是 MSVC 19.34.31944,以及线性代数库 Eigen 的最新版本 (3.4.0)。 编译选项是
/Oxt /std:c++20 /EHs
加上包含 Eigen 所在的位置。
第一个例子:
#include "Eigen/Dense"
#include <iostream>
std::pair<Eigen::Vector2d, double> func() {
return { Eigen::Vector2d{11, 0}, 0 };
};
int main(int argc, char *argv[]) {
const Eigen::Vector2d &result = func().first;
std::cout << *result.data() << " " << *(result.data() + 1) << std::endl;
std::cout << result.transpose() << std::endl;
std::cout << func().first.transpose() << std::endl;
return 0;
}
这里我应该得到一个等于[11, 0]的向量,所以输出应该是:
11 0
11 0
11 0
但是,我有:
11 0
6.95158e-310 6.95145e-310
11 0
我无法在调试中或在 Linux 中使用 g++ 重现此行为(这意味着我得到了正确的行为)。此外,如果我在某个时候添加
std::cout << result << std::endl
(没有 .transpose()
),错误就会消失。所有这些让我认为这是一些内存错误。
起初我认为这是因为结果是保存为常量引用的右值的成员,但它似乎是有效的。请注意,不通过引用获取结果会给出预期的行为,但由于看似无用的打印也隐藏了错误,我无法断定此引用业务就是原因。
然后我认为这是由于
transpose
函数的错误使用造成的。然后我想到了第二个例子:
#include "Eigen/Dense"
#include <iostream>
#include <numbers>
// Computes the angle between two 2D vectors in [-pi, pi]
inline double computeAngle(const Eigen::Vector2d &vec1, const Eigen::Vector2d &vec2) {
double angle = std::atan2(vec2(1), vec2(0)) - std::atan2(vec1(1), vec1(0));
angle = angle > std::numbers::pi ? angle - 2.0 * std::numbers::pi : angle;
angle = angle < -std::numbers::pi ? angle + 2.0 * std::numbers::pi : angle;
return angle;
}
std::pair<Eigen::Vector2d, double> func() {
double coeffMid = std::sin(2 * std::abs(computeAngle(Eigen::Vector2d{ 5, -5 }, Eigen::Vector2d{ -5, -5 })));
std::cout << coeffMid << std::endl;
Eigen::Vector2d center = (Eigen::Vector2d{ 10, 0 } + Eigen::Vector2d{ 0, 0 }) / 2;
return { Eigen::Vector2d{5, 0}, 0 };
}
int main(int argc, char *argv[]) {
const Eigen::Vector2d &result = func().first;
for (size_t i = 0; i < 5; i++) {
const Eigen::Vector2d foo = result + result;
std::cout << foo[0] << " " << foo[1] << std::endl;
}
return 0;
}
我希望到达的地方
1.22465e-16
10 0
10 0
10 0
10 0
10 0
但是我有
1.22465e-16
10 0
20 0
40 0
80 0
160 0
这个例子没有多大意义,因为它最初是一个较大的例子,后来被简化了。有些行看起来没用(在 func 中,
computeAngle
中的三元组),但它们确实改变了行为。
在我看来,我错过了一些明显的东西,因为这两个例子都很小,但我不知道是什么。我知道用 const 引用捕获函数输出并不流行,我已阅读并理解原因,但标准仍然允许它,因此它不是此错误的原因。不然我没看懂标准!
有人对此有一些见解吗?
提前致谢
我写了一个关于表达式模板的较长答案,但删除了它,因为我意识到我错了。
这只是依赖于未定义行为的代码。在计算表达式
func
时,临时寿命延长会延长 func().first
结果的寿命。之后,result
是一个悬空引用,取消引用它可以做任何事情。