创建临时时std :: function坏内存访问

DiT*_*TiD 7 c++ geometry segmentation-fault c++11

我目前正在实现几个抽象来表示3D对象的水平集操作.基本上这个 GLSL着色器的惊人页面中描述了什么.

为了简要概述,可以通过将R ^ 3域映射到称为水平集(或符号距离函数)的标量的函数来描述3D对象.例如,对于球体,水平集函数由phi(X) = X.Norm2() - R*Rwhere Norm2表示R ^ 3中的矢量的平方欧几里德范数来定义.

所以我推出了一个LevelSetObject代表这样一个概念的类:

 class LevelSetObject
    {

    using SDFFunction = std::function<double(double, double, double)>;

    protected:
        SDFFunction m_SDF;

    public:

        double SDF(double x, double y, double z) const {
            return m_SDF(x, y, z);
        }
Run Code Online (Sandbox Code Playgroud)

现在我想在LevelSetObjects 之间定义一些运算符.例如union运算符:

LevelSetObject LevelSetObject::operator+(const LevelSetObject& other) const {
        LevelSetObject outObj;
        outObj.m_SDF = [this, other]
            (double x, double y, double z) {
                return std::min(m_SDF(x, y, z), other.m_SDF(x, y, z));
            };
        return outObj;
    }
Run Code Online (Sandbox Code Playgroud)

但是当我创建一个临时的时,我遇到了错误的内存访问,例如,由于三次总和(如果我在注释的情况下单独对两个对象求和,则使用Valgrind而不是SIGSEV没有发现内存泄漏).LevelSetSphere是一个派生类LevelSetObject,我在这里定义了一个球体的SDF(给定它center和它的radius)

int main(int argc, char* argv[]) {

    // Generate the central sphere
    double radius = 1.0;
    SimpleVector center(2, 2, 2);
    LevelSetSphere sphere(radius, center);

    // Generate the ears spheres
    LevelSetSphere ear1(radius/2, SimpleVector(1, 1, 2));
    LevelSetSphere ear2(radius/2, SimpleVector(3, 1, 2));

    // Combine objects
    auto mickeyMouse = sphere + ear1 + ear2;
    //auto first = sphere + ear1;
    //auto mickeyMouse = first + ear2;

    // Materialize in the domain

    mickeyMouse.SDF(0.0, 0.0, 0.0);



}
Run Code Online (Sandbox Code Playgroud)

我认为在operator+定义中,当我实际调用时,std::function保持对它的引用other变为悬空引用,m_SDF因为在三元和期间创建了临时.我也试图改变的签名operator+operator+(const LevelSetObject other),所以通过复制传递,但结果是一样的.

我哪里失败了?:)

Sam*_*hik 10

派生类中的lambda捕获this到派生类,并将其推送到基类中std::function.

这是一个麻烦的秘诀.

这意味着,至少,派生类必须完全符合Rule 3,并且至少实现一个复制构造函数和一个赋值运算符,它精心重新安装一个新的lambda,this实际上是一个新捕获的指的是派生类的正确实例.

如果您有一个std::function类的成员,它捕获它自己this的类,并且该类被复制,则捕获的this不会自动更新以引用该类的新实例.C++不能这样工作.新类的std::functionthis仍引用类的原始实例.如果一个类的实例是从该类的另一个实例分配的,请猜猜是什么?复制的std::function捕捉到的this仍然指向复制,从类的实例.

但我真的没有看到任何std::function花园式虚拟功能无法实现的功能.只需用m_SDF虚拟功能替换,这一切都会消失.


Jos*_*and 7

您的错误内存访问不是由于other变量,它是this临时对象超出范围的指针.

您可以通过显式捕获SDF变量来修复它,如下所示:

LevelSetObject LevelSetObject::operator+(const LevelSetObject& other) const {
        LevelSetObject outObj;
        auto& SDF=this->m_SDF;
        auto& other_SDF=other.m_SDF
        outObj.m_SDF = [SDF, other_SDF]
            (double x, double y, double z) {
                return std::min(SDF(x, y, z), other_SDF(x, y, z));
            };
        return outObj;
    }
Run Code Online (Sandbox Code Playgroud)