这样的签名有哪些有效的用例?
T&& foo();
那么,rvalue引用只用于作为参数吗?
如何使用这样的函数?
T&& t = foo(); // is this a thing? And when would t get destructed?
T&& foo();
那么,rvalue引用只用于作为参数吗?
如何使用这样的函数?
T&& t = foo(); // is this a thing? And when would t get destructed?
对于一个自由函数来说,返回右值引用并没有太多意义。如果它是一个非静态的局部对象,则永远不应该返回指向它的引用或指针,因为函数返回后它将被销毁。但是,将一个传递给函数的对象作为右值引用返回可能是有意义的,这真的取决于是否合理。
一个可以极大受益于返回右值引用的东西是临时对象的成员函数。假设你有一个
class foo
{
std::vector<int> bar;
public:
foo(int n) : bar(n) {}
std::vector<int>& get_vec() { return bar; }
};
如果你这样做
auto vec = foo(10).get_vec();
你必须复制,因为 get_vec
返回的是一个左值。如果你使用
class foo
{
std::vector<int> bar;
public:
foo(int n) : bar(n) {}
std::vector<int>& get_vec() & { return bar; }
std::vector<int>&& get_vec() && { return std::move(bar); }
};
那么vec
将能够移动由get_vec
返回的向量,并且您可以避免进行昂贵的复制操作。
std::move(a).get_vec()
)。我的意思是,你可能会返回一个即将被销毁的对象的引用。这与返回对局部函数变量的引用相同,存在相同的问题。 - Cruz Jeanstd::vector<int> get_vec() && { return std::move(bar); }
999/1000 次都更好。你能想出一个更好的例子吗? - Yakk - Adam Nevraumonta
仍然存在,所以v
仍然是对a
内部向量的有效引用。 - NathanOliverauto bar = foo{42}.get_vec();
这样使用的。 - NathanOliver&&
,那么例如std::optional
在operator*()
中为什么会这样做呢? - Jarek C
T&& t = foo(); // is this a thing? And when would t get destructed?
右值引用与左值引用非常相似。可以将其看作是普通引用的一个例子:
T& foo();
T& t = foo(); // when is t destroyed?
t
仍然有效。
对于您的右值引用示例,仍然适用相同的答案。
但是,返回一个右值引用有意义吗?
有时候会有意义,但非常少见。
考虑以下情况:
std::vector<int> v = ...;
// type is std::tuple<std::vector<int>&&>
auto parameters = std::forward_as_tuple(std::move(v));
// fwd is a rvalue reference since std::get returns one.
// fwd is valid as long as v is.
decltype(auto) fwd = std::get<0>(std::move(parameters));
// useful for calling function in generic context without copying
consume(std::get<0>(std::move(parameters)));
struct wrapper {
auto operator*() & -> Heavy& {
return heavy;
}
auto operator*() && -> Heavy&& {
return std::move(heavy);
}
private:
Heavy instance;
};
// by value
void use_heavy(Heavy);
// since the wrapper is a temporary, the
// Heavy contained will be a temporary too.
use_heavy(*make_wrapper());
operator*()
返回对“instance”的引用,该引用同样是临时的。因此,在函数返回后,您将拥有对生命周期已结束(未定义行为)的对象的引用。 - Cruz Jeandecltype(auto) fwd = std::get<0>(std::move(parameters));
吗? - Yakk - Adam NevraumontT&& std::optional<T>::operator*() &&
被定义成完全按照 @GuillaumeRacicot 所提出的那样运作。我本来期望的是T std::optional<T>::operator*() &&
,但实际上并不是这样... - Jarek C我认为一个使用案例是显式地授权“清空”一些非本地变量。可能像这样:
class Logger
{
public:
void log(const char* msg){
logs.append(msg);
}
std::vector<std::string>&& dumpLogs(){
return std::move(logs);
}
private:
std::vector<std::string> logs;
};
但我承认现在是编造的,我从未真正使用过它,也可以像这样完成:
std::vector<std::string> dumpLogs(){
auto dumped_logs = logs;
return dumped_logs;
}
return std::move(logs);
这里返回值是一个引用类型,而 *this
是一个临时对象(因为它是一个右值方法),这使得 this->logs
也成为了一个临时对象。这就是我所说的对临时对象的引用。 - Cruz Jeanauto&& x = Logger{}.dumpLogs();
吗?是的,那是悬空引用,但无论它们返回什么类型的引用,所有getter都会发生这种情况。我的用例将类似于 Logger l; /*调用log()*/, auto logs = l.dumpLogs();/*再次记录..并重复。*/
- Quimby
std::get
函数族。 - Brian Bi