我正在尝试编写一个基本的游戏引擎,并且遇到了有关动态组件创建的问题。我这里有一个最小的工作示例:
#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 并调用函数来证明),但该值从未设置。通过调试,我还向自己证明,引用向量的索引会产生正确的内存地址,因此指针应该是正确的。所以我的问题是:为什么值设置不正确,如何修复它?
问题在于捕获
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); };
}