【问题标题】:Returning tuple of local objects返回本地对象的元组
【发布时间】:2019-02-03 10:26:44
【问题描述】:

如何利用结构化绑定和元组来返回函数的本地对象?

在一个函数中,我正在创建相互引用的本地对象,并且我想在一个元组中返回这些对象,并在我调用函数时使用结构化绑定来识别它们。我目前有这个:

std::tuple<Owner&&, State<Controller>&&, State<Ancillary>&&, State<Compressor>&&>
inline makeOwner() {
    State<Controller>&& controller = State<Controller>();
    State<Ancillary>&&  ancillary  = State<Ancillary>();
    State<Compressor>&& compressor = State<Compressor>();

    Owner&& owner = Owner(controller, ancillary, compressor);

    return {owner, controller, ancillary, compressor};
}

// using the function later

const &&[owner, controller, ancillary, compressor] = makeOwner();

这不起作用,我收到一条错误消息,指出返回值不能转换为上述返回类型的元组。我不确定为什么会这样,因为类型与声明相匹配。

最终,我正在尝试创建一个方便的函数,这样我就不必在每次创建新所有者时都在函数中键入四行。这是我尝试使用结构化绑定使这更容易。

编辑: 我应该注意,我希望最后一行中的绑定引用所有者内部的对象。所以,副本是不够的。

【问题讨论】:

  • 请记住,右值引用仍然只是一个引用,返回对局部变量的引用是不好的。您应该能够以价值回报并享受 RVO 已经为您提供的性能。

标签: c++ templates c++17 move-semantics structured-bindings


【解决方案1】:

我希望最后一行中的绑定引用所有者内部的对象。

让我们忽略所有新的语言功能,回到基础。您希望它如何工作?

int&& f() { return 0; }
int&& r = f();

您希望r 成为对f 内部局部变量的引用吗?但这在f() 的执行结束时被销毁。此代码可以编译,但 r 是一个悬空引用。

唯一安全的方法是确保f() 返回一个对绝对比函数寿命更长的对象的引用。可能是本地的static,也可能是全局的,也可能是f所属的类的成员变量等:

int global = 0;
int&& f() { return std::move(global); }
int&& r = f(); // okay, r is a reference to global, no dangling

或者,如果这没有意义,那么您需要按值返回一个对象。您仍然可以参考它。与否:

int f() { return 0; }
int&& r = f(); // okay, lifetime extension
int i = f();   // okay, prvalue elision

一旦我们添加了tuple 和结构化绑定的所有复杂性,相同的基本原则就会适用。通过值返回本地的非静态对象,或者通过引用返回一些其他对象。但不要通过引用返回本地的非静态对象。


最终,我正在尝试创建一个方便的函数,这样我就不必每次想创建一个新的所有者时都在函数中键入四行。这是我尝试使用结构化绑定来简化此操作。

为什么不直接做一个类型呢?

struct X {
    X() : owner(controller, ancillary, compressor) { }
    X(X const&) = delete;
    X& operator=(X const&) = delete;

    State<Controller> controller;
    State<Ancillary>  ancillary;
    State<Compressor> compressor;
    Owner owner;        
};

// lifetime extension on the X, no copies anywhere
// note that owner is last
auto&& [controller, ancillary, compressor, owner] = X();

// no lifetime extension, but also no copies because
// prvalue elision
auto [controller, ancillary, compressor, owner] = X();

【讨论】:

  • 我会 =deleteX 进行移动/复制操作,因为我不是疯子。
【解决方案2】:
inline auto makeOwner() {
   struct bundle {
     State<Controller> controller;
     State<Ancillary> ancillary;
     State<Compressor> compressor;
     Owner owner = Owner(controller, ancillary, compressor);
     bundle(bundle  const&)=delete;
     bundle& operator=(bundle  const&)=delete;
   };
   return bundle{};
}

// using the function later

const auto&&[owner, controller, ancillary, compressor] = makeOwner();

在这里,我们使用了结构,甚至是匿名结构,都可以像元组一样分拆。

Live example.

【讨论】:

    猜你喜欢
    • 2018-02-21
    • 2011-02-03
    • 2015-10-09
    • 2015-07-26
    • 1970-01-01
    • 2017-10-28
    • 1970-01-01
    • 1970-01-01
    • 2010-10-22
    相关资源
    最近更新 更多