某日二师兄参加XXX科技公司的C++工程师开发岗位第16面:
面试官:什么是左值,什么是右值?
二师兄:简单来说,左值就是可以使用&符号取地址的值,而右值一般不可以使用&符号取地址。
- int a = 42; //a是左值,可以&a
- int* p = &a;
- int* p = &42; //42是右值,无法取地址
复制代码二师兄:一般左值存在内存中,而右值存在寄存器中。
- int a = 42, b = 1024;
- decltype(a+b); //类型为右值,a+b返回的值存在寄存器中
- decltype(a+=b); //类型为左值,a+=b返回的值存储在内存中
复制代码二师兄:严格意义上分,右值分为纯右值(pvalue)和将亡值(xvalue)。C++中,除了右值剩余的就是左值。
- 42; //纯右值
- int a = 1024;
- std::move(a); //将亡值
复制代码面试官:C++98/03中已经有了左值,为什么还要增加右值的概念?
二师兄:主要是为了效率。特别是STL中的容器,当需要把容器当作参数传入函数时:
- void function(std::vector<int> vi2)
- {
- vi2.push_back(6);
- for(auto& i: vi2) { std:: cout < i << " " ;}
- std::cout << std::endl;
- }
- int main(int argc, char* argv[])
- {
- std::vector<int> vi1{1,2,3,4,5};
- function(vi1);
- return 0;
- }
复制代码完美转发知道吗?
当我们需要在function中传递t参数时,如何保证它的左值或右值语义呢?这时候完美转发就登场了:- void function(std::vector<int>&& vi2)
- {
- vi2.push_back(6);
- for(auto& i: vi2) { std:: cout < i << " " ;}
- std::cout << std::endl;
- }
- int main(int argc, char* argv[])
- {
- std::vector<int> vi1{1,2,3,4,5};
- function(std::move(vi1));
- return 0;
- }
复制代码 当传入的参数t的类型时右值时,由于引用折叠还是右值,此时的t虽然时一个右值引用,但t本身却是一个左值!这里非常的不好理解。如果我们把t直接传入到function2,那么function2中的t2会被推导成左值,达不到我们的目标。如果在调用function2时传入std::move(t),当t是右值时没有问题,但当t是左值时,把t移动到t2,t在外部不在能用。这也不符合我们的预期。此时std::forward闪亮登场!- struct Foo
- {
- int* data_;
-
- //copy construct
- Foo(const Foo& oth)
- {
- data_ = new int(*oth.data_);
- }
- //move construct
- Foo(Foo&& oth) noexcept
- {
- data_ = oth.data_; //steal
- oth.data_ = nullptr; //set to null
- }
- }
复制代码 std::forward使用了编译时多态(SFINAE)技术,使得当参数t是左值是和右值是匹配不同的实现,完成返回不同类型引用的目的。以下是标准库的实现:- template<typename T>
- void function(T&& t) { ...}
复制代码 好了,今日份面试到这里就结束了。二师兄的表现如何呢?预知后事如何,且听下回分解。
关注我,带你21天“精通”C++!(狗头)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |