简介
Lvalue & Rvalue
- 左值可以运用&操作符取得地址,注意临时对象是无法取得地址的,因为很容易导致问题。
- 左值必然有一个名字
- 不是左值的是右值
int f();
int i;
int& g();
int&& h();&f; // f is lvalue, &f returns to pointer to the function
f(); // f() is rvalue, as f returns a int by value
i; // i is lvalue
g(); // g() is lvalue, as f returns a lvalue reference to int
h(); // h() is rvalue, as f returns a rvalue reference to intvoid f(int&& i)
{// i is a rvalue reference, but i is a lvalue as named rvalue reference is lvalue
}
这里需要注意的是函数的返回值,一个函数调用通常在返回值是左值引用的时候才是左值(见n3242 5.2.2/10,下面截取标准中的文字以供参考)。
5.2.2/10A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function
type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.
5/6In general, the effect of this rule is that named rvalue references are treated as lvalues and unnamed rvalue
references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether
named or not
Reference
左值引用形如T&,而右值引用形如T&&,并且我们知道右值引用可以绑定到右值,那么我们时候可以绑定到一个右值常量?因为常量是不可修改的,但是由于T&&不是reference to const,所以是否成立?
答案是可以的,请看如下例子:
#include <iostream>
using namespace std;int main()
{int&& rri = 5;rri = 4;cout << rri << endl;// error// char const*&& rrcc = "hello";// *rrcc = '1';
}
这里在g++4.7.3中运行后可以发现rri的值是4。
这里涉及到reference的初始化,标准中规定,对于使用右值来初始化一个右值引用,可以创建一个拷贝,即,这里5会被保存在一个对象中,所以这里我们对这个对象进行修改。标准中8.5.3/5中如下描述(部分):
8.5.3/5
— Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be
const), or the reference shall be an rvalue reference.
If the initializer exrepssion is xvalue or ...
— Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression
using the rules for a non-reference copy-initialization
再看下面注释中的代码,由于rrcc的类型是reference to pointer to const char,所以我们在后面对char const*赋值时出错。
Move Constructor
class foo
{
public:foo(foo&& f): m_s(f.m_s) // m_s(move(f.m_s)){}
private:string m_s;
};
这里,foo(foo&&)是move ctor,由于f是右值引用,我们认为,我们可以通过直接调用string的move ctor而不做任何处理。这是错误的。结果这里只有string的copy ctor被调用。
std::move
static_cast<T&&>(v)
当然具体实现会复杂一下,不过请继续接着看。
Perfect Forwarding
void f(foo const& a);
void f(foo&& a);
但是当参数数量变多时怎么办,假设有N个参数,那么显然我们需要重载2^N个函数才能解决问题,所以引入了perfect forwarding。
function template
void g(int const&);
void g(int&&);template<typename T>
void f(T&& v)
{g(forward<T>(v));
}
这里标准库函数forward完成了类型转发。抱枕传递给的g的类型的左右值属性是用户传入的属性。
14.8.2.1/3If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction. If P is a reference type, the type referred to by P is used for type deduction. If P is an rvalue reference to a cv-unqualified template parameter and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
Reference Collapsing Rule
- T& && = T&
- T&& & = T&
- T& & = T&
- T&& && = T&&
Deduction
Template <class T> int f(T&&);
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which// would bind an rvalue reference to an lvalue
先来看f(i)的调用,根据14.8.2.1/3,我们有
- P = T&&,由于P是右值引用,所以Deduced A = T
- 由于P是右值引用,并且i是左值,并且i的类型是int,所以Transformed A = int&
- Deduced A = Transformed A => T = int&
- P = T&&,由于P是右值引用,所以Deduced A = T
- 0是右值,所以Transformed A = int
- Deduced A = Transformed A => T = int
std::forward
template<typename T>
T&& forward(std::remove_reference<T>::type& v)
{return static_cast<T&&>(v);
}
又是一个函数模板。假设我们指定T = int&,那么将会有:
- remove_reference<T>::type& = int&
- static_cast<int& &&>(v) = static_cast<int&>(v)
- 返回 int& && = int&
- remove_reference<T>::type& = int&,这里还是左值引用,没错,还记得我们传递给forward的也是左值吗?虽然它是右值引用。
- static_cast<int&& &&>(v) = static_cast<int&&>(v)
- 返回int&& && = int&&
Explictly specifiec template parameter
template<typename T>
T&& forward(T& v)
{return static_cast<T&&>(v);
}
我们还是可以推导出,这里也能够实现完美转发(有的读者可能认为无法对参数是const的对象进行转发,事实不是如此,我们尽在f(T&&)内部使用,所以不存在这个问题,可以试着自己推导)。
template<typename T>
void f(T&& v)
{g(forward(v));
}
引用文档
- N3242,c++标准草案,有更新的版本,自行google。
- C++ Rvalue Reference Explained
- ACCU Overload 111 中的 Universal Reference