When not to use `auto&&`?

给你一囗甜甜゛ 提交于 2019-12-03 11:15:20

问题


  auto&& mytup = std::make_tuple(9,1,"hello");
  std::get<0>(mytup) = 42;
  cout << std::get<0>(mytup) << endl;
  1. Is there a copy/move involved (without RVO) when returning from make_tuple?
  2. Is it causing undefined behavior?
  3. I can both read write the universal reference. Can auto&& var = func() be used always instead of auto var = func() so that there is no copy/move?

回答1:


  1. Yes. Any return from a function that does not return a reference type may involve a copy/move. Eliding that is what RVO is about. The object that your reference is bound to needs to be initialized somehow.

  2. No. why should it? The lifetime of a temporary/prvalue bound to a reference is determined by the scope of the reference.

  3. If func() does not return a reference type, there should not be any difference in efficiency (nor behaviour)

between

auto&& var = func();

and

auto var = func();

In both cases an object with lifetime to the end of the containing block is constructed. In one case it has its own name, in the other it is named via a reference. In both cases the name can be used as an lvalue. RVO can be equally well applied in either case.

Some compilers might optimize better for a local object than for a reference, even though in the current case the reference-to-temporary is really no different from a local object.

If func() might return a reference, things are much different - in that case you must decide whether you want to copy/move or not.




回答2:


It's only ever problematic in the case where the initializer is a function call that returns a short-lived rvalue reference. With less words and more code:

// 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!

In contrast, auto uhoh = id(42); would have worked fine.

In your case, because std::make_tuple returns a value and not an rvalue reference there is no problem.

I'm of the opinion that the real danger is from those functions and function templates with rvalue reference parameters and that return an rvalue reference to either those of some subobjects which lifetimes depend on those. (That being said, something as simple as auto&& ref = std::move(42); exhibits the problem!)

The situation is not entirely new from C++11, consider: T const& ref = bar(T_factory());.




回答3:


There is a specific rule in C++ (even before C++11) that says that if you bind a reference to a temporary the lifetime of the temporary will be extended to the lifetime of the reference.

A simpler case is:

int foo () {
    return 42;
}

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



回答4:


The result of evaluating the call to make_tuple is a prvalue temporary of a tuple instantiation. The auto type-specifier will be inferred as the same tuple instantiation (7.1.6.4p6), so mytup is of type tuple<...> &&. The prvalue temporary is then lifetime-extended by the reference mytup (12.2p5).

  1. Because the temporary is the function's return value, there is no copy/move involved (There may still be RVO within make_tuple).
  2. The behaviour is fully defined.
  3. For almost all cases, mytup will be treated as an lvalue even though its type is an rvalue reference. However there is no point using auto && as all sensible compilers will elide the copy/move from the temporary.

To clarify, about the only way to have mytup treated as an rvalue is to use std::forward<decltype(mytup)>(mytup), as in What does auto&& tell us?; however if you know the type of mytup (tuple<...> in this case) then you may as well use std::move.



来源:https://stackoverflow.com/questions/14849107/when-not-to-use-auto

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!