左值与右值
什么是左值?什么是右值?
在C++里没有明确定义。看了几个版本,有名字的是左值,没名字的是右值。能被&取地址的是左值,不能被&取地址的是右值。而且左值与右值可以发生转换。
我个人的理解就是在当前作用域下右值是个临时变量。
举例如下:https://blog.csdn.net/wangshubo1989/article/details/50479162
// lvalues:
//
int i = 42;
i = 43; // ok, i is an lvalue
int* p = &i; // ok, i is an lvalue
int& foo();
foo() = 42; // ok, foo() is an lvalue
int* p1 = &foo(); // ok, foo() is an lvalue
// rvalues:
//
int foobar();
int j = 0;
j = foobar(); // ok, foobar() is an rvalue
int* p2 = &foobar(); // error, cannot take the address of an rvalue
j = 42; // ok, 42 is an rvalue
照搬了别人的例子。(尴尬)
右值引用
以前在拜读muduo的时候就遇到过右值引用T&&。当时没详细了解,就知道配合std::move使用,是移动构造函数A(A&& a)中使用的。
后来才知道他其实是右值引用
具体关于move以及移动构造函数我以前以及了解过,也使用比较多,此处不用记录了C++11中移动语义(std::move)和完美转发(std::forward)也有所解释,我更看中他对forward的解释(笑~)
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
class MoveTest {
public:
MoveTest(int i) :
_i(i) {
cout << "Normal Constructor" << endl;
}
MoveTest(const MoveTest& other) :
_i(other._i) {
cout << "Copy Constructor" << endl;
} // = delete;
MoveTest& operator=(const MoveTest& other) {
return *this;
} //= delete;
MoveTest(MoveTest&& o) {
_i = o._i;
cout << "Move Constructor" << endl;
}
MoveTest& operator=(const MoveTest&& o) {
cout << "Move Assign" << endl;
return *this;
}
//private:
int _i;
};
MoveTest get() {
MoveTest t = MoveTest(2);
cout << "get() construct" << endl;
return t; //MoveTest(1);
}
int main(int argc, char **argv) {
MoveTest t(get());
cout << t._i << endl;
t = get();
cout << "------------------------" << endl;
vector<MoveTest> vct_t;
vct_t.emplace_back(get());
cout << "------------------------" << endl;
vct_t.push_back(get());
cout << "------------------------" << endl;
std::move(get());
}
Normal Constructor
get() construct
2
Normal Constructor
get() construct
Move Assign
------------------------
Normal Constructor
get() construct
Move Constructor
------------------------
Normal Constructor
get() construct
Move Constructor
Copy Constructor
------------------------
Normal Constructor
get() construct
右值会被编译器匹配右值引用的函数。
注意
研究了好久,终于找到了vct_t.push_back(get());
为什么打印结果里有Copy Constructor。其实《STL源码剖析》有介绍过的,在容器预分配空间不够的时候,会申请两倍的空间,然后调用uninitialized_copy()把原有内容复制到新的空间,该函数会调用construct(T1*p const T2&value)为每一个元素产生一个复制,其实就是调用copy constructor。
经过实验,将vector预留足够空间能避免这次copy,讲push_back和emplace_back执行顺序交换copy也是在后一次插入中执行。
引用折叠 => 完美转发
什么是引用折叠?
来自C++11中移动语义(std::move)和完美转发(std::forward)
不过还是很难理解这和完美转发的实现有什么关联?为什么引用折叠能让forward完美转发参数原来的左值右值属性?
感觉是这文的的实例代码一段讲述了forward的好处,后一段其实是说明引用折叠的,所以我没看懂
//https://en.cppreference.com/w/cpp/utility/forward
#include <iostream>
#include <memory>
#include <utility>
struct A {
A(int&& n) { std::cout << "rvalue overload, n=" << n << "\n"; }
A(int& n) { std::cout << "lvalue overload, n=" << n << "\n"; }
};
class B {
public:
template<class T1, class T2, class T3> B(T1&& t1, T2&& t2, T3&& t3) :
a1_{std::forward<T1>(t1)},
a2_{std::forward<T2>(t2)},
a3_{std::forward<T3>(t3)}
{
}
private:
A a1_, a2_, a3_;
};
template<class T, class U> std::unique_ptr<T> make_unique1(U&& u)
{
return std::unique_ptr<T>(new T(std::forward<U>(u)));
}
template<class T, class... U> std::unique_ptr<T> make_unique2(U&&... u)
{
return std::unique_ptr<T>(new T(std::forward<U>(u)...));
}
int main()
{
auto p1 = make_unique1<A>(2); // rvalue
int i = 1;
auto p2 = make_unique1<A>(i); // lvalue
std::cout << "B\n";
auto t = make_unique2<B>(2, i, 3);
}
rvalue overload, n=2
lvalue overload, n=1
B
rvalue overload, n=2
lvalue overload, n=1
rvalue overload, n=3
结合此段代码就可以感觉到,虽然B(T1&&,T2&&,T3&&)的参数都是右值引用,结合应用折叠规则,其转入的参数都会保留原有的右值引用和左值引用属性。