什么时候不使用`auto&&`?

时间:2013-02-13 08:14:03

标签: c++ c++11 auto universal-reference

  auto&& mytup = std::make_tuple(9,1,"hello");
  std::get<0>(mytup) = 42;
  cout << std::get<0>(mytup) << endl;
  1. 返回时是否涉及复制/移动(没有RVO) make_tuple?
  2. 是否导致未定义的行为?
  3. 我都可以读写通用引用。可以auto&& var = func()始终使用auto var = func()代替{{1}},以便没有复制/移动吗?

4 个答案:

答案 0 :(得分:14)

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

// 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返回值而不是右值引用,所以没有问题。

我认为真正的危险来自具有右值参考参数的那些函数和函数模板,并且返回对某些子对象的rvalue引用,其寿命依赖于那些子对象。 (话虽如此,像auto&& ref = std::move(42);这样简单的事情就会出现问题!)

C ++ 11的情况并非全新,请考虑:T const& ref = bar(T_factory());

答案 1 :(得分:8)

  1. 是。从不返回引用类型的函数返回的任何内容都可能涉及复制/移动。这就是RVO的意义所在。您的引用绑定的对象需要以某种方式初始化。

  2. 没有。为什么要这样?绑定到引用的临时/ prvalue的生命周期由引用的范围确定。

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

  4. 之间

    auto&& var = func();
    

    auto var = func();
    

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

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

    如果func()可能会返回引用,则情况会有很大不同 - 在这种情况下,您必须决定是否要复制/移动。

答案 2 :(得分:0)

评估对make_tuple的调用的结果是tuple实例化的prvalue临时值。 auto 类型说明符将被推断为相同的tuple实例化(7.1.6.4p6),因此mytup的类型为tuple<...> &&。然后,通过引用mytup(12.2p5)对prvalue临时进行生命周期扩展。

  1. 因为临时函数的返回值,所以不涉及复制/移动(make_tuple内可能仍有RVO。)
  2. 行为已完全定义。
  3. 对于几乎所有情况,mytup将被视为左值,即使其类型是右值引用。然而,使用auto &&毫无意义,因为所有合理的编译器都会忽略临时复制/移动。
  4. 澄清一下,将mytup视为右值的唯一方法是使用std::forward<decltype(mytup)>(mytup),如What does auto&& tell us?;但是,如果您知道 mytup的类型(在这种情况下为tuple<...>),那么您也可以使用std::move

答案 3 :(得分:0)

C ++中有一个特定的规则(甚至在C ++ 11之前),如果你将一个引用绑定到临时,那么临时的生命周期将扩展到引用的生命周期。

更简单的情况是:

int foo () {
    return 42;
}

int bar () {
    const int& x = foo();
    return x; // Here is safe to use x
}