auto&& mytup = std::make_tuple(9,1,"hello");
std::get<0>(mytup) = 42;
cout << std::get<0>(mytup) << endl;
- 从 make_tuple 返回时是否涉及复制/移动(没有 RVO)?
- 它会导致未定义的行为吗?
- 我都可以读写通用参考。可以
auto&& var = func()
一直使用而不是auto var = func()
这样就没有复制/移动?
auto&& mytup = std::make_tuple(9,1,"hello");
std::get<0>(mytup) = 42;
cout << std::get<0>(mytup) << endl;
auto&& var =
func()
一直使用而不是auto var = func()
这样就没有复制/移动?只有在初始化程序是返回短期右值引用的函数调用的情况下,它才会出现问题。用更少的词和更多的代码:
// 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());
.
是的。任何不返回引用类型的函数的返回都可能涉及复制/移动。消除这就是 RVO 的意义所在。您的引用绑定到的对象需要以某种方式初始化。
不,为什么要这样做?绑定到引用的临时/prvalue 的生命周期由引用的范围决定。
如果 func() 不返回引用类型,则效率(或行为)不应该有任何差异
之间
auto&& var = func();
和
auto var = func();
在这两种情况下,都会构造一个生命周期到包含块末尾的对象。在一种情况下,它有自己的名称,在另一种情况下,它是通过引用命名的。在这两种情况下,名称都可以用作左值。RVO 同样适用于任何一种情况。
一些编译器对本地对象的优化可能比对引用的优化更好,即使在当前情况下,临时引用实际上与本地对象没有什么不同。
如果func()
可能返回一个引用,情况就大不相同了——在这种情况下,您必须决定是否要复制/移动。
C++ 中有一条特定规则(甚至在 C++11 之前),如果将引用绑定到临时,则临时的生命周期将延长到引用的生命周期。
一个更简单的情况是:
int foo () {
return 42;
}
int bar () {
const int& x = foo();
return x; // Here is safe to use x
}
评估调用的结果是实例化make_tuple
的prvalue 临时。类型说明符tuple
将被推断为相同的实例化(7.1.6.4p6),类型也是如此。然后,prvalue 临时值由引用(12.2p5) 延长生命周期。auto
tuple
mytup
tuple<...> &&
mytup
make_tuple
)。mytup
即使它的类型是右值引用,也会被视为左值。auto &&
然而,使用所有明智的编译器都会忽略临时的复制/移动是没有意义的。mytup
澄清一下,被视为右值的唯一方法是使用std::forward<decltype(mytup)>(mytup)
,如auto&& 告诉我们什么?; 但是,如果您知道mytup
(在这种情况下)的类型,tuple<...>
那么您也可以使用std::move
.