C++ 函数返回右值引用有意义吗?

C++ 函数返回右值引用有意义吗?,c++,C++,这样的签名的有效用例是什么 T&& foo(); 还是右值ref仅用作参数 如何使用这样的函数 T&& t = foo(); // is this a thing? And when would t get destructed? 我认为一个用例是显式地允许“清空”一些非局部变量。也许是这样的: class Logger { public: void log(const char* msg){ logs.append(msg);

这样的签名的有效用例是什么

T&& foo();
还是右值ref仅用作参数

如何使用这样的函数

T&& t = foo(); // is this a thing? And when would t get destructed?

我认为一个用例是显式地允许“清空”一些非局部变量。也许是这样的:

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;
}
类记录器
{
公众:
无效日志(常量字符*msg){
logs.append(msg);
}
std::vector&&dumpLogs(){
返回std::移动(日志);
}
私人:
向量日志;
};
但我承认我现在编造了这个,我从来没有真正使用过它,它也可以这样做:

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;
}
std::向量转储日志(){
自动转储的日志=日志;
返回转储的日志;
}

对于自由函数,返回右值引用没有多大意义。如果它是一个非静态的局部对象,那么您永远不希望返回指向它的引用或指针,因为它将在函数返回后被销毁。返回对传递给函数的对象的右值引用可能是有意义的。这实际上取决于用例是否合理

从返回右值引用中获益匪浅的一件事是临时对象的成员函数。假设你有

class foo
{
    std::vector<int> bar;
public:
    foo(int n) : bar(n) {}
    std::vector<int>& get_vec() { return bar; }
};
您必须复制,因为
get\u 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); }
};
class-foo
{
向量条;
公众:
foo(int n):bar(n){}
std::vector&get_vec()&{return bar;}
std::vector&&get_vec()&&{return std::move(bar);}
};
然后
vec
将能够移动
get_vec
返回的向量,从而节省昂贵的复制操作

右值引用实际上类似于左值引用。把你的例子想象成普通参考:

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)));

我想到了。它肯定会返回
T&
。编辑:还有一个
T&
重载。编辑2:它还有一个
常量&&
重载,尽管我承认我不理解它的意思。@FrançoisAndrieux
std::get
函数族,太好了。看。这不是转发引用吗?@FrançoisAndrieux,这在我的文章中有介绍。不过,返回对传递给函数的对象的右值引用可能是有意义的。这实际上取决于用例是否合理。一网打尽。我知道有些情况,但我真的不想尝试列出所有这些情况。与按值返回相比,是否有理由更喜欢按右值引用返回?@super我想你说的是
get\u vec
情况?如果按值返回,将导致2次移动操作。通过右值引用,您只有一次移动。@NathanOliver好的,您可以在任何右值上调用它(例如,
std::move(a).get_vec()
)。我的观点是,您可能返回对即将被销毁的对象的引用。这与返回对局部函数变量的引用的问题相同。
std::vector get_vec()&&{return std::move(bar);}
在这种情况下,999/1000更好。你能想出一个更好的例子吗?在最后一个例子中,如果你在上面使用的
包装器
实例是临时的,
操作符*()
返回对
实例
的引用,它同样是临时的。因此,在函数返回后,您有一个对其生存期已结束的对象的引用(未定义的行为)。您确定不需要
decltype(auto)fwd=std::get(std::move(parameters))?这也是其他答案得出的结果,但在对临时对象调用此函数的情况下,当函数返回时,您有一个(右值)引用,指向一个生命周期已结束的对象(未定义的行为)。@CruzJean嗯,我的答案首先在这里,没有人反对/抱怨,所以我将保留在这里。我不会在任何地方返回对临时文件的任何引用。
return std::move(logs)
其中返回值是引用类型,
*此
是临时的(由于是右值方法),使得
此->日志也成为临时的。这是对临时文件的引用。@cruzjen你是说例如
auto&&x=Logger{}.dumpLogs()?是的,这是悬空引用,但对于所有getter,无论它们返回什么类型的引用,都会发生这种情况。我的用例类似于
Logger l/*调用log()*/,auto logs=l.dumpLogs()/*再次登录。。然后重复。*/
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());