【问题标题】:std::tie and lifetimes of objects returned in tuple元组中返回的对象的 std::tie 和生命周期
【发布时间】:2017-07-11 13:51:13
【问题描述】:

在下面的代码中,我有一个函数可以修改两个复制成本高的对象,我试图在没有输出参数的情况下逃脱

   struct FatThing {/* some big data members here*/};

   auto processFatThings(FatThing ft1, FatThing ft2)-> std::tuple<FatThing, FatThing> {
      // do smth with those two
      return std::make_tuple(move(ft1), move(ft2));
   }

   auto useProcessFatThings()-> void {
      FatThing ft1, ft2;
      std::tie(ft1, ft2) = processFatThings(move(ft1), move(ft2)); // dangling references?
   }

我现在有点困惑,因为编译器不会发出任何警告,消毒剂和 memcheck 是干净的,并且代码可以正常工作。但!!!这里不是用 std::tie 创建的悬空引用吗?

【问题讨论】:

  • 你认为为什么会涉及到悬空引用?您没有在任何地方使用引用,无论是在函数参数中还是在返回类型中。

标签: c++ c++11 tuples lifetime


【解决方案1】:

不,没有悬空引用。 ft1ft2 将从 processFatThings 的返回值分配给各自的元素

例如看下面的代码

#include <iostream>
#include <tuple>
#include <type_traits>

using std::cout;
using std::endl;

class Something {
public:
    Something() {
        cout << __PRETTY_FUNCTION__ << endl;
    }
    Something(Something&&) {
        cout << __PRETTY_FUNCTION__ << endl;
    }
    Something(const Something&) {
        cout << __PRETTY_FUNCTION__ << endl;
    }
    ~Something() {
        cout << __PRETTY_FUNCTION__ << endl;
    }
    Something& operator=(const Something&) {
        cout << __PRETTY_FUNCTION__ << endl;
        return *this;
    }
};

int main() {
    Something one, two;
    std::tie(one, two) = std::make_tuple(Something(), Something());
}

现场演示https://wandbox.org/permlink/iG1qIJ2VKL4bljPM

在这里你会看到有 4 个结构,对应于onetwomake_tuple 的两个参数。

然后是make_tuple 的两个移动构造。

然后有两个副本分配。这是这里的关键部分。 Something 对象被复制到与std::tie“绑定”的任何地方。

所以没有悬空引用,你会得到副本/移动!

【讨论】:

  • 好吧,如果您添加移动分配,它将使用它。我仍然对它的工作原理有些困惑,但现在感觉好多了:)。谢谢!
【解决方案2】:

这里没有危险的引用。 ft1ft2 是值,我们将它们绑定到引用,ft1ft2(在块末尾过期)的生命周期超过了引用(在行尾过期)。

但是:

  auto ftings = processFatThings(move(ft1), move(ft2));
  FatThing& ft1 = std::get<0>(ftings);
  FatThing& ft2 = std::get<1>(ftings);

稍微好一点,因为它消除了移动结构。它也符合什么

  auto[ft1,ft2] = processFatThings(move(ft1), move(ft2));

在 C++17 中执行。

请注意,如果FatThing 拥有大数据成员,而不是拥有move 没有帮助。一个包含 10 亿个元素的数组需要 10 亿个单位的时间来移动。同时,一个包含 10 亿个元素的向量的数据成员的移动比复制要快得多。 (Vector 拥有它的缓冲区,它不会在其中存储它的缓冲区)

【讨论】:

    猜你喜欢
    • 2013-02-23
    • 2023-02-07
    • 1970-01-01
    • 2013-04-18
    • 2018-07-18
    • 2017-09-07
    • 2020-07-28
    • 1970-01-01
    相关资源
    最近更新 更多