如何使用引用捕获正确复制 lambda?
How to correctly copy a lambda with a reference capture?
好的,所以我 运行 在用 c++ 实现类似系统的 c# 属性 时遇到了问题(参见:)。
考虑以下示例:
struct TransformCmp
{
PropertyDelGetSet<vec3> position =
PropertyDelGetSet<vec3>(
[&]() -> const vec3& { return m_position; },
[&](const vec3& val) { m_position = val; m_dirty = true; });
private:
bool m_dirty = true;
vec3 m_position = vec3(0);
}
If/when T运行sformCmp 的实例是 copied/moved(例如,如果它存储在 std::vector
中并调用了调整大小),现在引用捕获无效。
问题是我如何确保 copy/move 发生时我也更新了参考捕获?
我已经尝试为 属性 类 实现一个复制构造函数,但我 运行 遇到了同样的问题,可能是因为我没有做对。有什么想法吗?
更新:
我正在尝试对 Matthias Grün 建议的仿函数做一个类似的想法,基本上我将 T运行sformCmp 指针传递给 属性DelGetSet 构造函数,它将被传递get-set函数。
初始化 属性 时,我正在做这样的事情:
PropertyDelGetSet<TransformCmp, vec3> position =
PropertyDelGetSet<TransformCmp, vec3>(this, // <- now passing this
[](TransformCmp* p) -> const vec3& { return p->m_position; },
[](TransformCmp* p, const vec3& val) { p->m_position = val; p->m_dirty = false; });
但是,我需要能够更新存储在 属性DelGetSet 中的指针才能使这项工作正常进行。
您可以创建仿函数而不是使用 lambda,方法如下:
struct Getter {
const vec3& operator()() const noexcept { return m_pRef->m_position; }
TransformCmp* m_pRef{};
};
struct Setter {
void operator()(const vec3& pos) noexcept { m_pRef->m_position = pos; }
TransformCmp* m_pRef{};
};
然后将这些实例传递给 PropertyDelGetSet
。在复制和移动期间,您可以更新 m_pRef
指针以指向正确的实例,有点像这样:
struct TransformCmp
{
...
TransformCmp(const TransformCmp& other) : position{ other.position }
position.getter().m_pRef = this;
}
...
}
假设 PropertyDelGetSet::getter()
将 return 一个 Getter&
通过它可以检索包含的仿函数。
无法从外部访问 Lambda 捕获,因为它们是 private
到 lambda。
下面是我们如何在我的家乡星球上执行此操作。
struct TransformCmp
{
void setPosition(const vec3& val) { m_position = val; m_dirty = true; }
vec3 getPosition() { return m_position; }
private:
bool m_dirty = true;
vec3 m_position = vec3(0);
};
看到了吗?没有 lambda,没有捕获,不需要更新任何东西,没有类型擦除的开销,什么都没有。
但是!但!但!我的设计呢?我有设计!有属性!和模板!和东西!
告诉你什么。 这个设计不行.
拥有内部管理对象引用的复杂事物(“属性”)本身并没有错。但是,如果您想将这些东西 存储在对象本身 中,这将无法通过代码审查。该对象有一个位置和一个脏标志。它们可以通过两个成员函数的绝对最小接口来操作,并且应该保持绝对最小。
如果需要一个类似于 属性 的对象,例如与其他类似界面的统一,则即时创建一个,使用它,并在对象有机会移动之前将其丢弃。它没有作为对象的一部分存储的业务。关注点分离。
我想我找到了最好的解决方案。
想法是从复制构造函数调用构造函数,然后手动设置其余可以简单复制的成员:
struct TransformCmp
{
TransformCmp() {}
TransformCmp(const TransformCmp& other)
: TransformCmp() // Makes sure the lambda refs are updated
{
// Trival copy of members
m_dirty = other.m_dirty;
m_position = other.m_position;
}
PropertyDelGetSet<vec3> position =
PropertyDelGetSet<vec3>(
[&]() -> const vec3& { return m_position; },
[&](const vec3& val) { m_position = val; m_dirty = false; });
private:
bool m_dirty = true;
vec3 m_position = vec3(0);
};
这样就不需要在 属性 类 中传递 TransformCmp 指针,这样会更简洁。如果有一种方法可以在重写后调用生成的复制构造函数,那就更干净了,但这对我来说已经很满意了。
好的,所以我 运行 在用 c++ 实现类似系统的 c# 属性 时遇到了问题(参见:)。
考虑以下示例:
struct TransformCmp
{
PropertyDelGetSet<vec3> position =
PropertyDelGetSet<vec3>(
[&]() -> const vec3& { return m_position; },
[&](const vec3& val) { m_position = val; m_dirty = true; });
private:
bool m_dirty = true;
vec3 m_position = vec3(0);
}
If/when T运行sformCmp 的实例是 copied/moved(例如,如果它存储在 std::vector
中并调用了调整大小),现在引用捕获无效。
问题是我如何确保 copy/move 发生时我也更新了参考捕获?
我已经尝试为 属性 类 实现一个复制构造函数,但我 运行 遇到了同样的问题,可能是因为我没有做对。有什么想法吗?
更新:
我正在尝试对 Matthias Grün 建议的仿函数做一个类似的想法,基本上我将 T运行sformCmp 指针传递给 属性DelGetSet 构造函数,它将被传递get-set函数。
初始化 属性 时,我正在做这样的事情:
PropertyDelGetSet<TransformCmp, vec3> position =
PropertyDelGetSet<TransformCmp, vec3>(this, // <- now passing this
[](TransformCmp* p) -> const vec3& { return p->m_position; },
[](TransformCmp* p, const vec3& val) { p->m_position = val; p->m_dirty = false; });
但是,我需要能够更新存储在 属性DelGetSet 中的指针才能使这项工作正常进行。
您可以创建仿函数而不是使用 lambda,方法如下:
struct Getter {
const vec3& operator()() const noexcept { return m_pRef->m_position; }
TransformCmp* m_pRef{};
};
struct Setter {
void operator()(const vec3& pos) noexcept { m_pRef->m_position = pos; }
TransformCmp* m_pRef{};
};
然后将这些实例传递给 PropertyDelGetSet
。在复制和移动期间,您可以更新 m_pRef
指针以指向正确的实例,有点像这样:
struct TransformCmp
{
...
TransformCmp(const TransformCmp& other) : position{ other.position }
position.getter().m_pRef = this;
}
...
}
假设 PropertyDelGetSet::getter()
将 return 一个 Getter&
通过它可以检索包含的仿函数。
无法从外部访问 Lambda 捕获,因为它们是 private
到 lambda。
下面是我们如何在我的家乡星球上执行此操作。
struct TransformCmp
{
void setPosition(const vec3& val) { m_position = val; m_dirty = true; }
vec3 getPosition() { return m_position; }
private:
bool m_dirty = true;
vec3 m_position = vec3(0);
};
看到了吗?没有 lambda,没有捕获,不需要更新任何东西,没有类型擦除的开销,什么都没有。
但是!但!但!我的设计呢?我有设计!有属性!和模板!和东西!
告诉你什么。 这个设计不行.
拥有内部管理对象引用的复杂事物(“属性”)本身并没有错。但是,如果您想将这些东西 存储在对象本身 中,这将无法通过代码审查。该对象有一个位置和一个脏标志。它们可以通过两个成员函数的绝对最小接口来操作,并且应该保持绝对最小。
如果需要一个类似于 属性 的对象,例如与其他类似界面的统一,则即时创建一个,使用它,并在对象有机会移动之前将其丢弃。它没有作为对象的一部分存储的业务。关注点分离。
我想我找到了最好的解决方案。
想法是从复制构造函数调用构造函数,然后手动设置其余可以简单复制的成员:
struct TransformCmp
{
TransformCmp() {}
TransformCmp(const TransformCmp& other)
: TransformCmp() // Makes sure the lambda refs are updated
{
// Trival copy of members
m_dirty = other.m_dirty;
m_position = other.m_position;
}
PropertyDelGetSet<vec3> position =
PropertyDelGetSet<vec3>(
[&]() -> const vec3& { return m_position; },
[&](const vec3& val) { m_position = val; m_dirty = false; });
private:
bool m_dirty = true;
vec3 m_position = vec3(0);
};
这样就不需要在 属性 类 中传递 TransformCmp 指针,这样会更简洁。如果有一种方法可以在重写后调用生成的复制构造函数,那就更干净了,但这对我来说已经很满意了。