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 int void f(int&& i) { // i is a rvalue reference, but i is a lvalue as named rvalue reference is lvalue }
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
左值引用形如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'; }
这里涉及到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*赋值时出错。
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被调用。
static_cast<T&&>(v)
void f(foo const& a); void f(foo&& a);但是当参数数量变多时怎么办,假设有N个参数,那么显然我们需要重载2^N个函数才能解决问题,所以引入了perfect forwarding。
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.
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
template<typename T> T&& forward(std::remove_reference<T>::type& v) { return static_cast<T&&>(v); }
template<typename T> T&& forward(T& v) { return static_cast<T&&>(v); }
template<typename T> void f(T&& v) { g(forward(v)); }