左值和右值的定义
从C中继承而来
左值:既能出现在等号左边也能出现在等号右边的变量(或表达式)。通常来说,有名字的变量即左值,且左值是在程序中能够寻址的东西。
右值:只能出现在等号右边的变量(或表达式)。由运算操作(加减乘除,函数调用返回值等)所产生的的中间结果(没有名字)即右值,右值是没法取得它的地址的。
在C++中的不同
对于内置的类型: 左值和右值的概念和C区别不大,右值是不可被修改的,也不可被const,volatile所修饰。
对于自定义的类型: 右值却允许通过它的成员函数进行修改,且能被const类型的引用所指向
左值引用,右值引用
举例代码:
class cs
{
public:
cs(int i): i_(i)
{
cout << "cs(" << i <<") constructor!" << endl;
}
~cs()
{
cout << "cs destructor,i(" << i_ << ")" << endl;
}
cs& operator=(const cs& other)
{
i_ = other.i_;
cout << "cs operator=()" << endl;
return *this;
}
int get_i() const
{
return i_;
}
void change(int i)
{
i_ = i;
}
private:
int i_;
};
cs get_cs()
{
static int i = 0;
return cs(i++);
}
int main()
{
// 合法
(get_cs() = cs(2)).change(323);
get_cs() = cs(2);// operator=()
get_cs().change(32);
return 0;
}
P.s.1 : 右值能被const类型的引用所指向
合法写法:
const cs& ref = get_cs();
Error:
cs& ref = get_cs();
即:当一个右值被const指向时,其生命周期就被延长(右值不能被当成左值使用,但左值能被当成右值使用)
P.s.2 : 非 const reference 不能引用 rvalue
void func(cs& c)
{
cout << "c:" << c.get_i() << endl;
}
//合法:
func(get_cs() = get_cs() );
//error
func(get_cs());
P.s.3 : 不同的拷贝复制函数
class cs
{
public:
cs& operator=(const cs& c);
};
// 另一种写法
class cs2
{
public:
cs2& operator=(cs2& c);
};
上面两种写法的不同之处就在于参数,一个是 const reference,一个是非 const。对于自定义类型的参数,通常来说,如果函数不需要修改传进来的参数,我们往往就按 const reference 的写法。
P.s.4 : 需要修改参数的值
// 类似auto_ptr
class auto_ptr
{
public:
auto_ptr(auto_tr& p)
{
ptr_ = p.ptr_;
p.ptr_ = NULL;
}
private:
void* ptr_;
};
所以,对于 auto_ptr 来说,它的 copy constructor 的参数类型是 non const reference。有些情况下,这种写法应该被鼓励,毕竟 non const reference 比 const reference 更能灵活应对各种情况,从而保持一致的接口类型,当然也有代价,参数的语义表达不准确了。除此更大的问题是如果拷贝构造函数写成这样子,却又对 rvalue 的使用带来了极大的不变,如前面所讲的例子,rvalue 不能被 non const reference 所引用,所以像 auto_ptr 的这样的类的 copy constructor 就不能接受 rvalue.
举个栗子:
//错误
auto_ptr p(get_ptr());
//operator=() 同理,错误
auto_ptr p = get_ptr();
这也是 auto_ptr 很不好用的原因之一,为了解决这个问题,c++11 中引入了一种新的引用类型,该种引用是专门用来指向 rvalue 的,有了这种新类型,对 lvalue 和 rvalue 的引用就能明确区分开来了。因为有了这种新的类型,接着就引出了 c++11 中新的语义,move(), forward() 等.