19
  auto&& mytup = std::make_tuple(9,1,"hello");
  std::get<0>(mytup) = 42;
  cout << std::get<0>(mytup) << endl;
  1. 从 make_tuple 返回时是否涉及复制/移动(没有 RVO)?
  2. 它会导致未定义的行为吗?
  3. 我都可以读写通用参考。可以auto&& var = func()一直使用而不是auto var = func()这样就没有复制/移动?
4

4 回答 4

15

只有在初始化程序是返回短期右值引用的函数调用的情况下,它才会出现问题。用更少的词和更多的代码:

// Fine; lifetime extension applies!
auto&& ref = 42;

auto id = [](int&& i) -> int&& { return std::move(i); };
auto&& uhoh = id(42);
// uhoh is now a stale reference; can't touch it!

相比之下,auto uhoh = id(42);会工作得很好。

在您的情况下,因为std::make_tuple返回一个值而不是右值引用,所以没有问题。

我认为真正的危险来自那些带有右值引用参数的函数和函数模板,它们返回一个右值引用,指向某些子对象的生命周期取决于这些子对象。(话虽这么说,auto&& ref = std::move(42);但问题很简单!)

从 C++11 开始,这种情况并不是全新的,请考虑:T const& ref = bar(T_factory());.

于 2013-02-13T08:39:47.850 回答
8
  1. 是的。任何不返回引用类型的函数的返回都可能涉及复制/移动。消除这就是 RVO 的意义所在。您的引用绑定到的对象需要以某种方式初始化。

  2. 不,为什么要这样做?绑定到引用的临时/prvalue 的生命周期由引用的范围决定。

  3. 如果 func() 不返回引用类型,则效率(或行为)不应该有任何差异

之间

auto&& var = func();

auto var = func();

在这两种情况下,都会构造一个生命周期到包含块末尾的对象。在一种情况下,它有自己的名称,在另一种情况下,它是通过引用命名的。在这两种情况下,名称都可以用作左值。RVO 同样适用于任何一种情况。

一些编译器对本地对象的优化可能比对引用的优化更好,即使在当前情况下,临时引用实际上与本地对象没有什么不同。

如果func()可能返回一个引用,情况就大不相同了——在这种情况下,您必须决定是否要复制/移动。

于 2013-02-13T08:43:13.750 回答
1

C++ 中有一条特定规则(甚至在 C++11 之前),如果将引用绑定到临时,则临时的生命周期将延长到引用的生命周期。

一个更简单的情况是:

int foo () {
    return 42;
}

int bar () {
    const int& x = foo();
    return x; // Here is safe to use x
}
于 2013-02-13T08:42:07.633 回答
0

评估调用的结果是实例化make_tuple的prvalue 临时。类型说明符tuple将被推断为相同的实例化(7.1.6.4p6),类型也是如此。然后,prvalue 临时值由引用(12.2p5) 延长生命周期。auto tuplemytuptuple<...> &&mytup

  1. 因为临时函数的返回值,所以不涉及复制/移动(可能仍然存在 RVO 内make_tuple)。
  2. 行为是完全定义的。
  3. 对于几乎所有情况,mytup即使它的类型是右值引用,也会被视为左值。auto &&然而,使用所有明智的编译器都会忽略临时的复制/移动是没有意义的。

mytup澄清一下,被视为右值的唯一方法是使用std::forward<decltype(mytup)>(mytup),如auto&& 告诉我们什么?; 但是,如果您知道mytup(在这种情况下)的类型,tuple<...>那么您也可以使用std::move.

于 2013-02-13T08:37:18.257 回答