?!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
左?lvalue),叛_?rvalue)是一个比较晦涩的概念Q有些h可能甚至没有听过Q但q个概念Cc++11后,却变得十分重要,它们是理解move(),forward(){新语义的基?/p>
那什么是左值右值呢Q?/strong>
左g叛_D两概忉|从c中传承而来的,在c中,左值指的是能够出现在等号左边及双的变?表达?, 叛_指的是只能出现在等号右边的变量(表达?.
复制代码
int a;
int b;
a = 3;
b = 4;
a = b;
b = a;
//不合法?/p>
3 = a;
a+b = 4;
复制代码
通常来说Q有名字的变量就是左?如上面例子中的a, b)Q而由q算(加减乘除Q函数调用返回值等)产生的中间结?没有名字)是叛_|如上?+4Q?a + b{?/p>
我们暂且可以认ؓQ左值就是在E序中能够寻值的东西Q右值就是没法取到它的地址的东?不完全准??/p>
如上概念Cc++中,变得稍有不同?/p>
在c++中,每一个表辑ּ都会产生一个左|或者右|相应的,该表辑ּ也就被称作“左D辑ּ”,“右D辑ּ”。对于内|的数据cd来说(build-in types)Q左值右值的概念和c的没有太多不同,不同的地方在于自定义的类型?/p>
而且q种不同比较Ҏ让hh:
1)对于内置的类型,叛_是不可被修改的(non-modifiable)Q也不可被const, volatile所修饰(cv-qualitification ignored)
2)对于自定义的cd(user-defined types), 叛_却允许通过它的成员函数q行修改?/p>
对于1),q是和c是一致的Q?)却是c++中所独有, 因此Q如果你看到c++中如下的写法Q也许有会些惊讶Q?/p>
复制代码
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;
}
复制代码
q个Ҏ多有些奇怪,通常来说Qc++中的自定义类型是应该设计地尽量和内置cd一h对的Q但q个Ҏ却偏偏q背了这个原则?/p>
对于q个Ҏ,我们其实可以q样惻I也许会好理解点:自定义类型允许有成员函数Q而通过叛_D用成员函数是被允许的Q但成员函数有可能不是constcdQ因此通过调用叛_的成员函数Q也可能会修改了该叛_|done!
关于叛_|q有一个需要注意的地方是:叛_D被constcd的引用所指向
const cs& ref = get_cs();
而且只能被const cd的reference所指向Q?/p>
//error
cs& ref = get_cs();
当一个右Dconst reference指向Ӟ它的生命周期p廉了,q个用法我在前面一博客里讲到q它的相兛_用,点这?/p>
q里暗藏逻辑其实是Q右g能直接{化成左?但左值可以{化ؓ叛_?.
上面提到的这两个Ҏ:
1)允许调用成员函数?/strong>
2)只能被const reference指向?/strong>
D了一些比较有意思的l果Q比如:
复制代码
void func(cs& c)
{
cout << "c:" << c.get_i() << endl;
}
//error
func(get_cs());
//正确
func(get_cs() = get_cs());
复制代码
其中:func(get_cs() = get_cs());能够正确的原因就在于Qcs的成员函数operator=() q回的是cs&Q?/p>
不允讔Rconst reference 引用rvalueq不是完的Q它事实上也引v了一些问题,比如说拷贝构造函数的接口不一致了Q这是什么意思呢Q?/p>
复制代码
class cs
{
public:
cs& operator=(const cs& c);
};
// 另一U写?/p>
class cs2
{
public:
cs2& operator=(cs2& c);
};
复制代码
上面两种写法的不同之处就在于参数Q一个是const referenceQ一个是非const.
通常来说Q如果不需要修改传q来的参敎ͼ我们往往按const reference的写法,但对于copy constructor来说Q经常是需要修改参数的|比如auto_ptr?/p>
复制代码
// cMauto_ptr
class auto_ptr
{
public:
auto_ptr(auto_tr& p)
{
ptr_ = p.ptr_;
p.ptr_ = NULL;
}
private:
void* ptr_;
};
复制代码
所以,对于auto_ptr来说Q它的copy constructor传的参数是non const reference?/p>
q个U写法本来应该被鼓励的,non const reference比const reference更能灉|应对各种情况Q从而保持一致的接口cd?/p>
但如果拷贝构造函数写成这样子Q却又对rvalue的用带来了极大的不变,如前面所讲的例子Qrvalue不能被non const reference所引用Q所以像auto_ptr的这Lcopy constructor׃能传入rvalue.
//错误
auto_ptr p(get_ptr());
// operator=() 同理Q错误?/p>
auto_ptr p = get_ptr();
q也是auto_ptr很不好用的其中一个原因?/p>
Z解决q个问题Qc++11中引入了一U新的引用类型,该种引用cd是专门用来指向rvalue的,有了q种新类型,在c++11中,lvalue 和rvalue的引用类型从此区分了开来,而在之前Q它们是一L?/strong>