您的位置:首页 > 编程语言 > C语言/C++

表达式左值右值(C++学习)

2011-08-06 20:43 369 查看
左值右值是表达式的属性,该属性称为 value category。按该属性分类,每一个表达式属于下列之一:

lvalue
left value,传统意义上的左值
xvalue
expiring value, x值,指通过“右值引用”产生的对象
prvalue
pure rvalue,纯右值,传统意义上的右值(?)
而 xvalue 和其他两个类型分别复合,构成:

lvalue + xvalue = glvalue
general lvalue,泛左值
xvalue + prvalue = rvalue
右值


区分?

++x 与 x++ 假定x的定义为 int x=0;,那么前者是 lvalue,后者是rvalue。前者修改自身值,并返回自身;后者先创建一个临时对像,为其赋值,而后修改x的值,最后返回临时对像。
区分表达式的左右值属性有一个简便方法:若可对表达式用 & 符取址,则为左值,否则为右值。比如

&obj , &*ptr , &ptr[index] , &++x
有效
&1729 , &(x + y) , &std::string("meow"), &x++
无效
对于函数调用,根绝返回值类型不同,可以是lvalue、xvalue、prvalue:

The result of calling a function whose return type is an lvalue reference is an lvalue

The result of calling a function whose return type is an rvalue reference is an xvalue.

The result of calling a function whose return type is not a reference is a prvalue.


const vs non-const

左值和右值表达式都可以是const或non-const。
比如,变量和函数的定义为:
string one("lvalue");
const string two("clvalue");
string three() { return "rvalue"; }
const string four() { return "crvalue"; }


那么表达式:

表达式
分类
one
modifiable lvalue
two
const lvalue
three()
modifiable rvalue
four()
const rvalue
引用

Type&
只能绑定到可修改的左值表达式
const Type&
可以绑定到任何表达式
Type&&
可绑定到可修改的左值或右值表达式
const Type&&
可以绑定到任何表达式


重载函数

#include <iostream>
#include <string>
using namespace std;

string one("lvalue"); const string two("clvalue"); string three() { return "rvalue"; } const string four() { return "crvalue"; }

void func(string& s)
{
cout << "func(string& s): " << s << endl;
}

void func(const string& s)
{
cout << "func(const string& s): " << s << endl;
}

void func(string&& s)
{
cout << "func(string&& s): " << s << endl;
}

void func(const string&& s)
{
cout << "func(const string&& s): " << s << endl;
}

int main()
{
func(one);
func(two);
func(three());
func(four());
return 0;
}


结果:
func(string& s): lvalue
func(const string& s): clvalue
func(string&& s): rvalue
func(const string&& s): crvalue


如果只保留const string& 和 string&& 两个重载函数,结果为:
func(const string& s): lvalue
func(const string& s): clvalue
func(string&& s): rvalue
func(const string& s): crvalue



右值引用

C++0x第5章的第6段:
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.


具名右值引用被视为左值
无名对对象的右值引用被视为x值
对函数的右值引用无论具名与否都将被视为左值

#include <iostream>
#include <string>

void F1(int&& a)
{
std::cout<<"F1(int&&) "<<a<<std::endl;
}

void F1(const int& a)
{
std::cout<<"F1(const int&) "<<a<<std::endl;
}

void F2(int&& a)
{
F1(a);
}

int main()
{
int && a=1;
F2(a);
F1(a);
F2(2);
F1(2);
return 0;
}


结果
F1(const int&) 1
F1(const int&) 1
F1(const int&) 2
F1(int&&) 2



移动语义

在这之前,如果写一个交换两个值的swap函数:
template <class T> swap(T& a, T& b)
{
T tmp(a);   // now we have two copies of a
a = b;      // now we have two copies of b
b = tmp;    // now we have two copies of tmp (aka a)
}


之后
template <class T> swap(T& a, T& b)
{
T tmp(std::move(a));
a = std::move(b);
b = std::move(tmp);
}


std::move 接受左值或右值参数,并返回一个右值(其所作工作很简单)
template <class T>
typename remove_reference<T>::type&&
move(T&& a)
{
return a;
}


要是的swap真正发挥作用,需要重载:
class T
{
public:
T(T&& );
T& operator = (T&& );
...



模板参数类型

为了对比左值引用和右值引用,一开始误打误撞,写了这样一个函数
template <typename Type> void Swap(Type&& sb1, Type&& sb2)
{
Type sb(sb1);
sb1 = sb2;
sb2 = sb;
}


然后
int main()
{
int a=1, b=2;
Swap(a, b);
std::cout<<a<<" "<<b<<std::endl;
return 0;
}


结果却是
2 2


不用整数,换用一个自定义的类型试试看:
class A
{
public:
A() {
std::cout << "Default constructor." << std::endl;
m_p = NULL;
}

~A() {
std::cout << "Destructor." << std::endl;
delete m_p;
}

explicit A(const int n) {
std::cout << "Unary constructor." << std::endl;
m_p = new int(n);
}

A(const A& other) {
std::cout << "Copy constructor." << std::endl;
if (other.m_p) {
m_p = new int(*other.m_p);
} else {
m_p = NULL;
}
}

A(A&& other) {
std::cout << "Move constructor." << std::endl;
m_p = other.m_p;
other.m_p = NULL;
}

A& operator=(const A& other) {
std::cout << "Copy assignment operator." << std::endl;
if (this != &other) {
delete m_p;
if (other.m_p) {
m_p = new int(*other.m_p);
} else {
m_p = NULL;
}
}
return *this;
}

A& operator=(A&& other) {
std::cout << "Move assignment operator." << std::endl;
if (this != &other) {
delete m_p;
m_p = other.m_p;
other.m_p = NULL;
}
return *this;
}

int get() const {
return m_p ? *m_p : 0;
}

private:
int * m_p;
};

int main()
{
A a(1);
A b(2);
Swap2(a, b);
std::cout<<a.get()<<" "<<b.get()<<std::endl;
return 0;
}


结果
Unary constructor.
Unary constructor.
Copy assignment operator.
Copy assignment operator.
2 2
Destructor.
Destructor.


只出现了两个对象,那么Swap中的临时对象去哪儿了?


C++0x 14.8.2.1

If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction. If P is a reference type, the type referred to by P is used for type deduction. If P is an rvalue reference to a cv unqualified template parameter and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.

template <class T> int f(T&&);
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which
// would bind an rvalue reference to an lvalue


也就是前面提到的
template <typename Type> void Swap(Type&& sb1, Type&& sb2)


参数推导后
void Swap<int&>(int& sb1, int& sb1)



参考

http://blog.csdn.net/zwvista/article/details/5459774

http://blog.csdn.net/hikaliv/article/details/4541429

http://topic.csdn.net/u/20090706/16/514af7e1-ad20-4ea3-bdf0-bfe6d34d9814.html

http://www.artima.com/cppsource/rvalue.html

PS:转自http://blog.csdn.net/dbzhang800/article/details/6663353
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息