c++ - 返回一个引用,但阻止它通过包装器或其他方式存储

标签 c++ architecture c++17

在我们的代码中,我们有时会返回对对象的非常量引用以链接方法调用。例如:

registry.GetComponent(entityID).GetParams().GetTuners().SetWidth(50)
有时在我们的评论中,我们会收到存储引用并对其进行处理的代码,这是我们禁止的。
auto& comp = registry.GetComponent(2);

// ... do something with component
// ... with our pooling, comp could invalid at any point
我们大部分时间都会捕获它,但有时它会通过并导致很难找到崩溃。我们尝试实现一个包装器(它在内部存储私有(private)引用)而不是引用,并且这个包装器是不可复制的。由于点运算符不能重载(在这种情况下会很完美),我们重载了 ()运算符。
所以现在我们有
// all GetXXXXX are now const ref
// registry.GetComponent(entityID).GetParams().GetTuners().SetWidth(50)
registry.UpdateComponent(entityID)().UpdateParams()().UpdateTuners()().SetWidth(50)

// this is still possible, but easier to catch in code reviews
auto comp = registry.UpdateComponent(entityID);
comp().UpdateParams()().UpdateTuners()().SetWidth(50);
丑陋,但在代码审查中更明显,如果存储在本地然后使用,在代码审查中也很容易发现。
是否可以返回对可修改对象的引用但阻止它被存储或允许我们安全地修改嵌套对象的不同模式。

Notes: The above reflect our code somewhat, but are very contrived examples. I hope the intent of the question is clear.

最佳答案

完全披露,我没有想太多。
如果要确保引用只能用于链接,返回 &&并仅针对右值 this 实现方法.

#include <utility>

struct Component{
    Component&& incValue() &&{return std::move(*this);}
    Component&& decValue() &&{return std::move(*this);}
    const Component&& print() const &&{return std::move(*this);} 
// Make copy,move ctors private to disallow copies if necessary.
};

struct Registry{
    Component test;
    const Component&& getConstComponent(){ return std::move(test); }
    Component&& getComponent(){ return std::move(test); }
};
int main()
{
    Registry registry;

    registry.getComponent().incValue().decValue().print();
    registry.getConstComponent().print();

    //ERROR, & cannot bind &&
    //Component& comp = registry.getComponent();
    //const & can bind &&
    const Component& comp = registry.getComponent();
    // ERROR, print only works with const &&
    // comp.print();
    // Yes, this works (and is perfectly valid code), it is not fool-proof. 
    std::move(comp).print();
    //BEWARE, this allows easy theft of the Component's data via move ctor.
    Component thief{registry.getComponent()};
    return 0;
}
我不建议这样做,我只说这是可能的,也许可以对其进行修改以帮助您。我认为没有多少开发人员熟悉基于 &,&& 的重载。它可能会变得困惑。
此外,它通过不进行任何资源转移来滥用移动语义。此外,它可以被 std::move 覆盖。或直接施法,但至少应该让用户暂停一下。
如果您使用 Component,这可能不起作用以正常方式的某个地方。一个不太好的解决方法是将左值方法设为私有(private)并使用 friend .更理智的版本是将此逻辑仅应用于包装器。缺点是 Component 的重复。的界面。
  • Registry::getComponent()按值返回 CompRef仅具有 r-value 方法的包装器,这些方法也按值返回自身的拷贝。这个包装器应该很小,所以希望 return CompRef by value+optimization == returning Component& .
  • 考虑制作拷贝,将 ctor 移动为私有(private)以禁止存储。由于每个拷贝都是在 CompRef 内部制作的本身。

  • 备注 :返回自身的 r 值只会在链中起作用,存储它会创建一个悬空引用,因为原始引用是临时的。这应该不是问题,因为您不应该真正使用它,但是任何访问都将成为 UB。
    一种中间方法,但非常具有侵入性,将是分区 Component通过继承进入 l-value 和 r-value(=chain-only) 方法。然后,Registry只能返回对组件的右值 View 的右值引用。这将允许在正常和仅链上下文中使用组件。
    同样,在放入任何代码库之前,这需要更多的思考。

    关于c++ - 返回一个引用,但阻止它通过包装器或其他方式存储,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63886743/

    相关文章:

    c++ - 根据赋值实例化模板函数

    c++ - 如何准确找到标记的中心?

    c# - 三层 Entity Framework 应用程序中的 DTO

    c++ - 测试元素是否使用 C++17 折叠表达式排序

    c++ - OpenGL header 中的平台特定宏

    c++ - 将复制构造函数设置为 private 和 =delete 有什么区别?

    architecture - 您如何在 Udi 风格的 SOA 架构中聚合数据?

    unit-testing - 如何组织集成测试?

    c++ - 为什么 std::get<T> for `variant` 是一个全局函数?

    c++ - 计算可变参数模板元组中的 std::optional 类型