c++c++11eigeneigen3

Wrong result in Eigen when returning a expression from within a lambda


I'm trying to use the body of this function in a lambda:

Vector3d fun(Vector3d const& point) {
    Vector3d const b{ 0., 0., 30. };
    return b + point.normalized();
}

but when the result is not explicitly evaluated before returning, either with

return (b + point.normalized()).eval();

or

Vector3d ret = b + point.normalized();
return ret;

the result is wrong for a lambda like

auto lamb = [](Vector3d const& point) {
    Vector3d const b{ 0., 0., 30. };
    return b + point.normalized();
    };

and i don't understand why.

The docs note that returning objects by value is ok and i'm not sure if and how to apply what's noted here.

I'm trying to understand why with this example body a lambda needs the explicit eval before return but an ordinary free function doesn't.

Short Demo

Long Demo

Code of the short demo:

#include <Eigen/Dense>
#include <iostream>

using Vector3d = Eigen::Vector3d;

Vector3d fun(Vector3d const& point) {
    Vector3d const b{ 0., 0., 30. };
    return b + point.normalized();
}

int main()
{
    auto lamb = [](Vector3d const& point) {
        Vector3d const b{ 0., 0., 30. };
        return b + point.normalized();
        //Vector3d ret = b + point.normalized(); return ret;
        };

    Vector3d const point{ 1., 2., 3. };

    Vector3d const resultl = lamb(point);
    std::cout << resultl.x() << " / " << resultl.y() << " / " << resultl.z() << "\n";

    Vector3d const resultf = fun(point);
    std::cout << resultf.x() << " / " << resultf.y() << " / " << resultf.z() << "\n";
}

Solution

  • return b + point.normalized(); returns an Eigen expression type that stores references to the two operands.

    In your free function, your return type is Vector3d which results in the expression being evaluated inside the function and then returning the result.

    In the lambda, the return type is auto deduced and as such, you're returning the Eigen expression itself. Then, then b and point go out of scope and their references inside the returned expression become dangling.

    This is fixed by evaluating the Eigen expression before returning, either by manually assigning or casting it to a Vector3d or by explicitly setting the return type of the lambda:

    auto lamb = [](Vector3d const& point) -> Vector3d {
        Vector3d const b{ 0., 0., 30. };
        return b + point.normalized();
        //Vector3d ret = b + point.normalized(); return ret;
        };
    

    Live Demo