我正在尝试编写一个基本的游戏引擎,并且遇到了一个关于动态组件创建的问题。我这里有一个最小的工作示例:
#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;
}
(Note格式方面的任何怪异都来自于这样一个事实,即为了方便起见,我的大多数组件都是用大量宏定义的。)
这将结束打印0
并返回,当它应该打印3
时。
现在的问题是我有一个子类的组件列表(这里表示为一个转换向量),我需要从中获取一个。在运行时,我不知道这个类是什么,所以我通过引用元素的索引作为Component指针来获取它。当我的代码调用_setters[<property>]
链接的函数时,它确实按预期进入了函数(通过将print函数放在lambda和called函数中来证明),但值从未设置。通过调试,我还向自己证明了引用向量的索引会产生正确的内存地址,因此指针应该是正确的。所以我的问题是为什么没有正确设置值,我该如何修复它?
1条答案
按热度按时间xpszyzbs1#
问题是捕获
this
然后复制Transform
对象。在
Transform()
构造函数中,表达式_setters["x"] = [=](Property p){this->__set_x(p); };
捕获this
。这捕获了this
在捕获发生时的立即值,它不能捕获“当前对象”的概念。当你构造
ts
时,你使用了一个初始化器列表,它需要复制初始化器。在这个复制过程中,_setters
被复制,包括由原始对象的默认构造函数设置的setter。它捕获的this
仍然指向原始对象。后来,当您调用
c->_setters["x"](3.0);
时,您成功地运行了c
所指向的Transform
的setter,但是这个setter是通过复制另一个Transform
获得的。setter捕获的this
仍然指向初始化列表中的原始示例。它不指向c
。setter试图为一个不再存在的示例设置x
的值,导致Undefined Behaviour。一个可能的解决方案是实现一个复制构造函数,它覆盖
"x"
setter来捕获新示例的this
。范例:但是,这种解决方案会重复代码,并且在您自己在外部覆盖
"x"
的情况下可能会很脆弱。您应该检查您的设计,可能会向setter添加一个“this”参数,以便任何示例都可以将自己的this
传递给该函数。