I have some c++ code that showed an error and I have not be able to understand why. I have however managed to reduce the code to a small example.
I am using MSVC 19.34.31944, and the last release (3.4.0) of the linear algebra library Eigen.
The compilation options are /Oxt /std:c++20 /EHs
plus the include of wherever Eigen is.
The first example:
#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;
}
Here I should get a vector equal to [11, 0], so the output should be:
11 0
11 0
11 0
However, I have:
11 0
6.95158e-310 6.95145e-310
11 0
I cannot reproduce this behavior (meaning I get the correct one) in debug, or with g++ in Linux. Furthermore, if I add std::cout << result << std::endl
(without .transpose()
) at some point the bug disappear. All this leads me to think this is some memory error.
At first I thought it was due to the fact that the result is a member of a rvalue that is saved as a constant reference but it seems to be valid. Note that getting the result not by reference gives the expected behavior, but since a seemingly useless print also hides the bug I cannot conclude this reference business is the cause.
Then I thought it is due to an incorrect usage of the transpose
function. I then came to this second example:
#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;
std::cout << &result << std::endl;
for (size_t i = 0; i < 5; i++) {
const Eigen::Vector2d foo = result + result;
std::cout << &foo << " " << foo[0] << " " << foo[1] << std::endl;
}
return 0;
}
where I hope to get
1.22465e-16
0000001954B3F9B0
0000001954B3F990 10 0
0000001954B3F990 10 0
0000001954B3F990 10 0
0000001954B3F990 10 0
0000001954B3F990 10 0
but I have
1.22465e-16
00000025FB4FFEA0
00000025FB4FFEA0 10 0
00000025FB4FFEA0 20 0
00000025FB4FFEA0 40 0
00000025FB4FFEA0 80 0
00000025FB4FFEA0 160 0
This example does not make a lot of sense, because it was initially a larger one that was simplified. Some lines seems useless (in func, the ternaries in computeAngle
) but they do change the behavior.
EDIT: I have slightly modified the example so that it shows the memory addresses of result
and foo
. It seems that the program uses the same address for both variable, which explains the wrong behavior. However, it seems to me that this (saving the member first
of the output as a const reference) should be valid.
It seems to me I am missing something obvious, as both example are quite small, but I cannot figure what. I know it is not popular to capture a function output with a const reference, I have read and understand why, but still it is allowed by the standard so it is not the cause of this bug. Or else I did not understand the standard!
Does anybody have some insight on this?
Thanks in advance
At some point I thought this was more related to Eigen so I asked my question on their gitlab. Here is the link to the corresponding issue. They also seem to think it is related to a MSVC bug.