为什么右值引用参数不能直接在函数之间传递?
Why the rvalue reference parameter cannot be passed between functions directly?
我的代码如下:
#include <iostream>
using namespace std;
class A{
public:
void sendByRvalue(string&& str){
cout << str << endl;
}
};
class B{
private:
A a;
void send(string&& str){
a.sendByRvalue(str);
}
public:
void run(const string& str){
send("run " + str + "\n");
}
};
int main(void){
string str("hello world");
B b;
b.run(str);
return 0;
}
当我编译上面显示的代码时,出现了一些编译错误:
B::send
函数中的str
好像改成左值了。然后我改变B::send
的实现,比如:
class B{
private:
A a;
void send(string&& str){
cout << boolalpha << is_rvalue_reference<decltype(str)>::value << endl;
a.sendByRvalue(std::move(str));
}
public:
void run(const string& str){
send("run " + str + "\n");
}
};
一切顺利,但这个程序的输出让我更加困惑。输出如下:
为什么参数 str 是一个右值引用,但我不能在没有 std::move
的情况下直接将它传递给函数 A::sendByRvalue
?
str
是命名的右值引用,在语言中被视为左值。右值只是 xvalues 或 prvalues,str
两者都不是。
来自 the standard 的注释,关于 xvalue 规则:
In general, the effect of this rule is that named rvalue references are treated as lvalues and unnamed rvalue references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether named or not.
struct A {
int m;
};
A&& operator+(A, A);
A&& f();
A a;
A&& ar = static_cast<A&&>(a);
The expressions f()
, f().m
, static_cast<A&&>(a)
, and a + a
are xvalues. The expression ar
is an lvalue.
我举个简单的例子来说明
void bar(std::string&& name) {
std::cout << "Bar: " << name << std::endl;
}
void foo(std::string&& name) {
bar(name);
}
int main() {
foo("C++");
}
这与您提供的示例大致相同。在foo()
、name
里面是一个左值。作为 rvalue 的临时字符串 C++
被传递到 name
。 name
是一个左值。所以上面的代码基本翻译成.
void bar(std::string&& name) {
std::cout << "Bar: " << name << std::endl;
}
int main()
{
std::string foo{ "C++" };
bar(foo);
}
现在问题出在哪里似乎很明显了。
我写了这个简单的程序来帮助你更好地理解
void foo(const std::string& name) {
std::cout << "Passed as lvalue ref: " << name << std::endl;
}
void foo(std::string&& name) {
std::cout << "Passed as rvalue ref: " << name << std::endl;
}
我们将使用这个函数来调用
void bar(std::string&& name) {
foo(name);
}
int main() {
bar("C++");
}
Passed as lvalue ref: C++
这证明了我的观点,现在如果我们使用std::move()
呢?
void bar(std::string&& name) {
foo(std::move(name));
}
int main() {
bar("C++");
}
Passed as an rvalue ref: C++
如果你想要一种简单的方法来检查一个表达式是左值还是右值,你可以试试下面的代码片段
template <typename T>
constexpr bool is_lvalue(T&) {
return true;
}
template <typename T>
constexpr bool is_lvalue(T&&) {
return false;
}
我的代码如下:
#include <iostream>
using namespace std;
class A{
public:
void sendByRvalue(string&& str){
cout << str << endl;
}
};
class B{
private:
A a;
void send(string&& str){
a.sendByRvalue(str);
}
public:
void run(const string& str){
send("run " + str + "\n");
}
};
int main(void){
string str("hello world");
B b;
b.run(str);
return 0;
}
当我编译上面显示的代码时,出现了一些编译错误:
B::send
函数中的str
好像改成左值了。然后我改变B::send
的实现,比如:
class B{
private:
A a;
void send(string&& str){
cout << boolalpha << is_rvalue_reference<decltype(str)>::value << endl;
a.sendByRvalue(std::move(str));
}
public:
void run(const string& str){
send("run " + str + "\n");
}
};
一切顺利,但这个程序的输出让我更加困惑。输出如下:
为什么参数 str 是一个右值引用,但我不能在没有 std::move
的情况下直接将它传递给函数 A::sendByRvalue
?
str
是命名的右值引用,在语言中被视为左值。右值只是 xvalues 或 prvalues,str
两者都不是。
来自 the standard 的注释,关于 xvalue 规则:
In general, the effect of this rule is that named rvalue references are treated as lvalues and unnamed rvalue references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether named or not.
struct A {
int m;
};
A&& operator+(A, A);
A&& f();
A a;
A&& ar = static_cast<A&&>(a);
The expressions
f()
,f().m
,static_cast<A&&>(a)
, anda + a
are xvalues. The expressionar
is an lvalue.
我举个简单的例子来说明
void bar(std::string&& name) {
std::cout << "Bar: " << name << std::endl;
}
void foo(std::string&& name) {
bar(name);
}
int main() {
foo("C++");
}
这与您提供的示例大致相同。在foo()
、name
里面是一个左值。作为 rvalue 的临时字符串 C++
被传递到 name
。 name
是一个左值。所以上面的代码基本翻译成.
void bar(std::string&& name) {
std::cout << "Bar: " << name << std::endl;
}
int main()
{
std::string foo{ "C++" };
bar(foo);
}
现在问题出在哪里似乎很明显了。
我写了这个简单的程序来帮助你更好地理解
void foo(const std::string& name) {
std::cout << "Passed as lvalue ref: " << name << std::endl;
}
void foo(std::string&& name) {
std::cout << "Passed as rvalue ref: " << name << std::endl;
}
我们将使用这个函数来调用
void bar(std::string&& name) {
foo(name);
}
int main() {
bar("C++");
}
Passed as lvalue ref: C++
这证明了我的观点,现在如果我们使用std::move()
呢?
void bar(std::string&& name) {
foo(std::move(name));
}
int main() {
bar("C++");
}
Passed as an rvalue ref: C++
如果你想要一种简单的方法来检查一个表达式是左值还是右值,你可以试试下面的代码片段
template <typename T>
constexpr bool is_lvalue(T&) {
return true;
}
template <typename T>
constexpr bool is_lvalue(T&&) {
return false;
}