左值和右值并不专属于STL里的内容,是在接触STL的过程发现了笔者C/C++的知识规则漏洞。
左值右值
左值(LValue)即等号左边的值,右值(RValue)即等号右边的值,右值必须放在等号右边,但左值既可以在左边也可以放在右边。那么数值(等式),字符串,常量,只能作为右值,右值决不能放置等号左边。
100 = 60; /*数值是右值,不合法*/
'A' = 'a'; /*字符是右值,不合法*/
const int a = 1;
a = 100; /*a为常量,属右值,不合法*/
变量,引用(reference)作为左值,既可以在等式左边,又可以在等式右边。
int a,b;
a = 2; /*a左值,2右值。*/
b = a; /*b左值,a左值。*/
int &c = a; /*a左值,c左值。*/
特别的,自增有两种形式:i++和++i,但两者是有区别的,允许我用c将其操作展开:
i++的操作:
{
int temp = i;
++i;
return temp;
}
++i的操作:
{
i = i + 1;
return i;
}
因此i++ = 1是不合法的,因为i++返回的是临时值,不是i自己,为了消除歧义,i++坚决返回右值,也就是说它只能放置在等式右边。而++i = 1是合法的,从上面操作的展开来看,++i确实返回了它自己,因此它是一个左值,既能是在等式的左边,也能是右边。
C++左值右值延伸
延伸至类的运算符重载的问题上。我们先假定一个类Node,
class Node
{
public:
Node(int nAge = 0)
{
m_nAge = nAge;
}
int GetAge()const
{
return m_nAge;
}
Node operator ++ (int n) /*i++*/
{
Node temp = *this;
m_nAge ++;
return temp;
}
Node& operator ++ () /*++i*//*你知道为什么要返回reference吗?*/
{
m_nAge ++;
return *this;
}
Node& operator = (int n)
{
m_nAge = n;
return *this;
}
private:
int m_nAge;
};
C++规定,Node& operator ++ ()是重载前缀自增运算符(++i),而Node operator ++ (int n)是重载后缀自增运算符(i++)。细心发现,重载前缀自增运算符返回的是reference引用,而重载后缀自增运算符返回的是临时变量。换句话说,如果有Node对象node,我希望++node(前缀)返回的是左值,node++(后缀)返回的右值。意即希望,注意是希望:
++node = 1; /*合法,++node返回值作为左值*/
node++ = 1; /*不合法,node++返回值作为左值*/
但是,重载运算操作符本来就是为改变运算符的行为而来的,所以上述行为是编译器所允许的。但语法上没有问题,但逻辑上却有严重的漏洞。++node = 1;确实改变了node的内容,但node++ = 1;未能得逞,因为“=1”的操作被执行在temp上,故**node++ = 1;执行过后,node内容改变为以前的值+1,而不是等于1。
......
Node node(23); /*node.m_nAge初值为23。*/
node ++ = 1;
cout << "node ++ = 1;执行过后,node.m_nAge = " << node.GetAge() << endl;
++ node = 1;
cout << "++ node = 1;执行过后,node.m_nAge = " << node.GetAge() << endl;
......
node ++ = 1;执行过后,node.m_nAge = 24
++ node = 1;执行过后,node.m_nAge = 1
请按任意键继续. . .
node++ = 1;执行过后,node没有被“=1”影响。那可不可以反其道而行呢?“我偏要让它返回左值”!当然行,没有问题,只要修改它的行为就好了。
......
Node& operator ++ (int n) /*i++*//*修改i++的行为,让它也返回左值*/
{
//Node temp = *this;
//m_nAge ++;
//return temp;
m_nAge ++;
return *this;
}
......
node ++ = 1;执行过后,node.m_nAge = 1
++ node = 1;执行过后,node.m_nAge = 1
请按任意键继续. . .
但还是建议不要这样做,因为会引起混淆。
总结下,左值可以出现在等式左边,又可以是右边;右值只能出现在右边。++i返回左值,i++返回右值,这样也就是为什么在重载前缀自增运算符时候,要返回reference(左值)了。
本文完 2012-10-23
Dylan http://daoluan.github.io/
23 October 2012 会持续更新