为什么我的“setter”函数存储在 std::map 中,无法正确设置值?

问题描述 投票:0回答:1

我正在尝试编写一个基本的游戏引擎,并且遇到了有关动态组件创建的问题。我这里有一个最小的工作示例:

#include <bitset>
#include <variant>
#include <map>
#include <functional>

#include <iostream>

using Property = std::variant<int, unsigned int, bool, double, std::string>;

class Component
{
public:
    std::map<std::string, std::function<void(Property)>> _setters;
    Component() {}

    virtual void DestroyComponent() {}
};

class Transform : public Component {
    public: static std::string name() { return "Transform"; }
    void __set_x(Property p) {
        x = std::get<double>(p);
    }
    public: double x = 0;
    
    public: Transform() {
        _setters["x"] = [=](Property p){this->__set_x(p); };
    }
};

int main()
{
    std::vector<Transform> ts = {Transform()};
    Component* c = &ts[0];

    c->_setters["x"](3.0);
    std::cout << ts[0].x << std::endl;

    return 0;
}

(请注意,格式方面的任何奇怪之处都来自于这样一个事实:为了方便起见,我的大多数组件都使用大量宏进行了定义。)

这将最终打印

0
并在应该打印
3
时返回。

现在的问题是这样的:我有一个子类的组件列表(这里表示为变换向量),我需要获取其中一个。在运行时,我不知道该类是什么,因此我通过引用元素的索引作为组件指针来获取它。当我的代码调用由

_setters[<property>]
链接到的函数时,它确实按预期进入该函数(通过将 print 函数放入 lambda 并调用函数来证明),但该值从未设置。通过调试,我还向自己证明,引用向量的索引会产生正确的内存地址,因此指针应该是正确的。所以我的问题是:为什么值设置不正确,如何修复它?

c++ pointers reference polymorphism
1个回答
2
投票

问题在于捕获

this
然后复制您的
Transform
对象。

Transform()
构造函数中,表达式
_setters["x"] = [=](Property p){this->__set_x(p); };
捕获
this
。这捕获了发生捕获时
this
的即时值,它无法捕获“当前对象”的概念。

当您构造

ts
时,您使用一个初始值设定项列表,该列表需要复制初始值设定项。在此复制过程中,
_setters
被复制,包括原始对象的默认构造函数设置的 setter。它捕获的
this
仍然指向原始对象。

后来,当你调用

c->_setters["x"](3.0);
时,你成功运行了
Transform
指向的
c
的setter,但是这个setter是通过复制另一个
Transform
获得的。 setter 捕获的
this
仍然指向初始化列表中的原始实例。它并不指向
c
。 setter 尝试为不再存在的实例设置
x
的值,从而导致未定义的行为。

您可以通过添加复制构造函数来解决该问题。

Transform(Transform const& copy) : Component(copy) {
    _setters["x"] = [=](Property p){this->__set_x(p); };
}
© www.soinside.com 2019 - 2024. All rights reserved.