#include <iostream>
#include <string>
using namespace std;
class Wrapper
{
public:
std::string&& get() &&
{
std::cout << "rvalue" << std::endl;
return std::move(str);
}
const std::string& get() const &
{
std::cout << "lvalue" << std::endl;
return str;
}
private:
std::string str;
};
std::string foo()
{
Wrapper wrap;
return wrap.get();
}
std::string bar()
{
Wrapper wrap;
return std::move(wrap.get());
}
std::string fooRvalue()
{
return Wrapper().get();
}
std::string barRvalue()
{
return std::move(Wrapper().get());
}
int main() {
while(1)
{
std::string s = foo();
std::string s2 = bar();
std::string s3 = fooRvalue();
std::string s4 = barRvalue();
}
return 0;
}
const std :: string&amp;和std :: string&amp;&amp;的返回值在这个用例中是安全的(假装std :: string是一些可能无法复制的其他类型)。我认为它不会起作用,因为引用应该指向一些超出范围的局部变量,但它似乎工作得很好?我也见过&amp;&amp;之前使用过的语法,我是否正确使用它(我将它限制为当Wrapper本身是一个右值时)。
我只是有点混淆当返回引用是安全的时候,我认为规则是对象必须比返回的值更长,但我已经看过像标准库这样的东西并且之前提升了returing引用。如果我存储引用它们给我一个变量(这不是一个引用),如果我然后返回该存储的变量,它似乎都工作正常。有人可以解决我的问题,并给我一些好的规则来跟进。
这是我正在玩的想法,一切似乎都有效:http://ideone.com/GyWQF6
答案 0 :(得分:3)
在销毁return
表达式中创建的局部变量和临时值之前,函数的返回值对象将被初始化。因此,从本地/临时值转换为函数返回值是完全合法的。
这四个全局函数可以工作,因为它们返回对象,而不是对象的引用。并且该对象在其构造函数参数引用被销毁的任何本地之前构造。
barRvalue
相当多余(因为Wrapper().get()
的结果已经是右值引用),但功能正常。
在返回引用是安全的时候,我只是有点困惑
返回对已销毁的对象的引用的函数是不安全的。返回对本地对象或本地对象的一部分的引用是不安全的。
返回对*this
或*this
的某些部分的引用是安全的,因为this
对象将比返回引用的函数更长。它与返回对指针/引用所引用参数的引用没有什么不同。该对象将比返回引用的函数调用更长。
答案 1 :(得分:3)
是的,这是实现成员getter的正确而安全的方法。但是你可以做一个改进,我会得到。关键是const user$ = name => // returns a new observable
Observable.create(o =>
gun.get(name).on(v => {
o.next(v); // passes any new values to the observers
console.log(v);
}),
);
// now you can do rx stuff on the stream of values
user$('something'))
.map(({ name }) => ({ name: name.toUpperCase() }))
.filter(({ name }) => name.length > 0)
成员的生命周期(几乎)与包含它的std::string
对象的生命周期相同。而临时只会在它出现的完整表达结束时被销毁,而不是在它被“使用”之后立即销毁。因此,在Wrapper
中,return std::move(Wrapper().get());
被创建,Wrapper
被调用,get()
被调用,返回值被构造,只有std::move
及其Wrapper
string
被摧毁。
唯一的危险是,如果某人绑定了对您的成员的另一个引用。但这就是他们的错误,而普通的const-reference getters也有同样的危险。这相当于错误的const char* ptr = "oops"s.c_str();
。
std::string foo()
{
Wrapper wrap;
return wrap.get();
}
foo
copy构造成员的返回值。希望这不是一个惊喜。
std::string bar()
{
Wrapper wrap;
return std::move(wrap.get());
}
bar
也复制构造成员的返回值。这里发生的事情是,由于wrap
是左值,因此调用左值get()
,返回const
左值。 (非正式地,“const std::string&
”。)然后move
将其转换为x值,但它仍然是const
。 (非正式地,“const std::string&&
”。)移动构造函数string(string&&)
在这里不匹配,因为参数是const
。但是复制构造函数string(const string&)
可以匹配,并且被称为。
如果您希望bar
移动构造返回值,您可能需要为非const左值添加第三个重载:
std::string& get() & { return str; }
(如果您不想允许修改该成员,请注意您已经完成了某些操作。例如,有人可以std::move(wrap).get() = "something";
。)
此外,如果你改为return std::move(wrap).get();
,即使没有第三次重载,也会移动构造返回值。
std::string fooRvalue()
{
return Wrapper().get();
}
fooRvalue
move构造返回值,因为使用了右值get()
。如前所述,Wrapper
的寿命足以使这种结构安全。
std::string barRvalue()
{
return std::move(Wrapper().get());
}
在barRvalue
中,move
来电绝对没用。表达式Wrapper().get()
已经是xvalue,因此move
只需将类型为std::string
的xvalue转换为... {x}类型的xvalue。尽管如此,仍然是安全的。