这样安全吗?右值和引用 return 值
Is this safe? Rvalue and reference return values
#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& 和 std::string&& 的 return 值在此用例中是否安全(假装 std::string 是其他一些可能不可复制的类型).我认为它不会工作,因为引用应该指向一些超出范围的局部变量,但它似乎工作得很好?我也看过以前使用的 && 语法,我是否正确使用它(我将其限制为 Wrapper 本身是右值时)。
当 return 引用是安全的时候,我只是有点困惑,我认为规则是对象必须比被 returned 的值长,但我见过类似的东西之前的标准库和提升引用。如果我将他们给我的引用存储在一个变量中(这不是引用),如果我然后 return 那个存储的变量,它似乎一切正常。谁能帮我解决一下,给我一些好的规则让我遵守。
这是我正在玩的 ideone,似乎一切正常:http://ideone.com/GyWQF6
函数的 return 值对象将在 在 销毁在 return
表达式中创建的局部变量和临时变量之前初始化。因此,从 locals/temporaries 移动到函数 return 值是完全合法的。
这四个全局函数起作用是因为它们 return 对象,而不是对对象的引用。并且该对象在其构造函数参数引用被销毁的任何局部变量之前构建。
barRvalue
相当多余(因为 Wrapper().get()
的结果已经是右值引用),但功能正常。
Im just a little generally confused when returning a reference is safe
一个函数return引用一个对象已经被销毁是不安全的。返回对局部对象或局部对象的一部分的引用是不安全的。
返回对 *this
或 *this
的某些部分的引用是安全的,因为 this
对象将比 return 引用的函数更长寿。这与 return 对您通过 pointer/reference 获取的参数的引用没有什么不同。该对象将比 return 引用的函数调用更有效。
是的,这是实现成员的正确且安全的方式getter。但是你可以做一个改进,我会谈到的。关键是 std::string
成员的生命周期(几乎)与包含它的 Wrapper
对象的生命周期相同。临时变量只会在它出现的完整表达式的末尾被销毁,而不是在 "used" 之后立即被销毁。所以在return std::move(Wrapper().get());
中,创建Wrapper
,调用get()
,调用std::move
,构造return值,然后才[=16] =] 及其 string
被摧毁。
唯一的危险是如果有人将另一个引用绑定到您的成员。但那将是他们的错误,普通的 const-reference getters 也有同样的危险。这相当于不正确的 const char* ptr = "oops"s.c_str();
.
std::string foo()
{
Wrapper wrap;
return wrap.get();
}
foo
复制从成员构造 return 值。希望这不是一个惊喜。
std::string bar()
{
Wrapper wrap;
return std::move(wrap.get());
}
bar
也从成员复制构造 return 值。这里发生的事情是因为 wrap
是一个左值,所以调用左值 get()
,return 是一个 const
左值。 (非正式地,“const std::string&
”。)然后 move
将其转换为一个 xvalue,但它仍然是 const
。 (非正式地,“const std::string&&
”。)移动构造函数 string(string&&)
无法在此处匹配,因为参数是 const
。但是拷贝构造函数string(const string&)
可以匹配,并被调用。
如果您希望 bar
移动构造 return 值,您可能需要为非常量左值添加第三个重载:
std::string& get() & { return str; }
(如果您不想允许修改成员,请注意您已经这样做了。例如,有人可以这样做 std::move(wrap).get() = "something";
。)
此外,如果您有 return std::move(wrap).get();
,即使没有第三次重载,也可以移动构造 return 值。
std::string fooRvalue()
{
return Wrapper().get();
}
fooRvalue
移动构造了 return 值,因为使用了右值 get()
。如前所述,Wrapper
的寿命足以使该结构安全。
std::string barRvalue()
{
return std::move(Wrapper().get());
}
在barRvalue
中,move
调用完全没用。表达式 Wrapper().get()
已经是一个 xvalue,所以 move
只是将类型 std::string
的 xvalue 转换为 ... std::string
类型的 xvalue。不过还是一样安全。
#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& 和 std::string&& 的 return 值在此用例中是否安全(假装 std::string 是其他一些可能不可复制的类型).我认为它不会工作,因为引用应该指向一些超出范围的局部变量,但它似乎工作得很好?我也看过以前使用的 && 语法,我是否正确使用它(我将其限制为 Wrapper 本身是右值时)。
当 return 引用是安全的时候,我只是有点困惑,我认为规则是对象必须比被 returned 的值长,但我见过类似的东西之前的标准库和提升引用。如果我将他们给我的引用存储在一个变量中(这不是引用),如果我然后 return 那个存储的变量,它似乎一切正常。谁能帮我解决一下,给我一些好的规则让我遵守。
这是我正在玩的 ideone,似乎一切正常:http://ideone.com/GyWQF6
函数的 return 值对象将在 在 销毁在 return
表达式中创建的局部变量和临时变量之前初始化。因此,从 locals/temporaries 移动到函数 return 值是完全合法的。
这四个全局函数起作用是因为它们 return 对象,而不是对对象的引用。并且该对象在其构造函数参数引用被销毁的任何局部变量之前构建。
barRvalue
相当多余(因为 Wrapper().get()
的结果已经是右值引用),但功能正常。
Im just a little generally confused when returning a reference is safe
一个函数return引用一个对象已经被销毁是不安全的。返回对局部对象或局部对象的一部分的引用是不安全的。
返回对 *this
或 *this
的某些部分的引用是安全的,因为 this
对象将比 return 引用的函数更长寿。这与 return 对您通过 pointer/reference 获取的参数的引用没有什么不同。该对象将比 return 引用的函数调用更有效。
是的,这是实现成员的正确且安全的方式getter。但是你可以做一个改进,我会谈到的。关键是 std::string
成员的生命周期(几乎)与包含它的 Wrapper
对象的生命周期相同。临时变量只会在它出现的完整表达式的末尾被销毁,而不是在 "used" 之后立即被销毁。所以在return std::move(Wrapper().get());
中,创建Wrapper
,调用get()
,调用std::move
,构造return值,然后才[=16] =] 及其 string
被摧毁。
唯一的危险是如果有人将另一个引用绑定到您的成员。但那将是他们的错误,普通的 const-reference getters 也有同样的危险。这相当于不正确的 const char* ptr = "oops"s.c_str();
.
std::string foo()
{
Wrapper wrap;
return wrap.get();
}
foo
复制从成员构造 return 值。希望这不是一个惊喜。
std::string bar()
{
Wrapper wrap;
return std::move(wrap.get());
}
bar
也从成员复制构造 return 值。这里发生的事情是因为 wrap
是一个左值,所以调用左值 get()
,return 是一个 const
左值。 (非正式地,“const std::string&
”。)然后 move
将其转换为一个 xvalue,但它仍然是 const
。 (非正式地,“const std::string&&
”。)移动构造函数 string(string&&)
无法在此处匹配,因为参数是 const
。但是拷贝构造函数string(const string&)
可以匹配,并被调用。
如果您希望 bar
移动构造 return 值,您可能需要为非常量左值添加第三个重载:
std::string& get() & { return str; }
(如果您不想允许修改成员,请注意您已经这样做了。例如,有人可以这样做 std::move(wrap).get() = "something";
。)
此外,如果您有 return std::move(wrap).get();
,即使没有第三次重载,也可以移动构造 return 值。
std::string fooRvalue()
{
return Wrapper().get();
}
fooRvalue
移动构造了 return 值,因为使用了右值 get()
。如前所述,Wrapper
的寿命足以使该结构安全。
std::string barRvalue()
{
return std::move(Wrapper().get());
}
在barRvalue
中,move
调用完全没用。表达式 Wrapper().get()
已经是一个 xvalue,所以 move
只是将类型 std::string
的 xvalue 转换为 ... std::string
类型的 xvalue。不过还是一样安全。