c++ - What is "rvalue reference for *this"? -


came across proposal called "rvalue reference *this" in clang's c++11 status page.

i've read quite bit rvalue references , understood them, don't think know this. couldn't find resources on web using terms.

there's link proposal paper on page: n2439 (extending move semantics *this), i'm not getting examples there.

what feature about?

first, "ref-qualifiers *this" "marketing statement". type of *this never changes, see bottom of post. it's way easier understand wording though.

next, following code chooses function called based on ref-qualifier of "implicit object parameter" of function:

// t.cpp #include <iostream>  struct test{   void f() &{ std::cout << "lvalue object\n"; }   void f() &&{ std::cout << "rvalue object\n"; } };  int main(){   test t;   t.f(); // lvalue   test().f(); // rvalue } 

output:

$ clang++ -std=c++0x -stdlib=libc++ -wall -pedantic t.cpp $ ./a.out lvalue object rvalue object 

the whole thing done allow take advantage of fact when object function called on rvalue (unnamed temporary, example). take following code further example:

struct test2{   std::unique_ptr<int[]> heavy_resource;    test2()     : heavy_resource(new int[500]) {}    operator std::unique_ptr<int[]>() const&{     // lvalue object, deep copy     std::unique_ptr<int[]> p(new int[500]);     for(int i=0; < 500; ++i)       p[i] = heavy_resource[i];      return p;   }    operator std::unique_ptr<int[]>() &&{     // rvalue object     // garbage anyways, move resource     return std::move(heavy_resource);   } }; 

this may bit contrived, should idea.

note can combine cv-qualifiers (const , volatile) , ref-qualifiers (& , &&).


note: many standard quotes , overload resolution explanation after here!

† understand how works, , why @nicol bolas' answer @ least partly wrong, have dig in c++ standard bit (the part explaining why @nicol's answer wrong @ bottom, if you're interested in that).

which function going called determined process called overload resolution. process complicated, we'll touch bit important us.

first, it's important see how overload resolution member functions works:

§13.3.1 [over.match.funcs]

p2 set of candidate functions can contain both member , non-member functions resolved against same argument list. argument , parameter lists comparable within heterogeneous set, a member function considered have parameter, called implicit object parameter, represents object member function has been called. [...]

p3 similarly, when appropriate, context can construct argument list contains implied object argument denote object operated on.

why need compare member , non-member functions? operator overloading, that's why. consider this:

struct foo{   foo& operator<<(void*); // implementation unimportant };  foo& operator<<(foo&, char const*); // implementation unimportant 

you'd want following call free function, don't you?

char const* s = "free foo!\n"; foo f; f << s; 

that's why member , non-member functions included in so-called overload-set. make resolution less complicated, bold part of standard quote exists. additionally, important bit (same clause):

p4 non-static member functions, type of implicit object parameter is

  • “lvalue reference cv x” functions declared without ref-qualifier or & ref-qualifier

  • “rvalue reference cv x” functions declared && ref-qualifier

where x class of function member , cv cv-qualification on member function declaration. [...]

p5 during overload resolution [...] [t]he implicit object parameter [...] retains identity since conversions on corresponding argument shall obey these additional rules:

  • no temporary object can introduced hold argument implicit object parameter; and

  • no user-defined conversions can applied achieve type match it

[...]

(the last bit means can't cheat overload resolution based on implicit conversions of object member function (or operator) called on.)

let's take first example @ top of post. after aforementioned transformation, overload-set looks this:

void f1(test&); // match lvalues, linked 'void test::f() &' void f2(test&&); // match rvalues, linked 'void test::f() &&' 

then argument list, containing implied object argument, matched against parameter-list of every function contained in overload-set. in our case, argument list contain object argument. let's see how looks like:

// first call 'f' in 'main' test t; f1(t); // 't' (lvalue) can match 'test&' (lvalue reference)        // kept in overload-set f2(t); // 't' not rvalue, can't match 'test&&' (rvalue reference)        // taken out of overload-set 

if, after overloads in set tested, 1 remains, overload resolution succeeded , function linked transformed overload called. same goes second call 'f':

// second call 'f' in 'main' f1(test()); // 'test()' not lvalue, can't match 'test&' (lvalue reference)             // taken out of overload-set f2(test()); // 'test()' (rvalue) can match 'test&&' (rvalue reference)             // kept in overload-set 

note that, had not provided ref-qualifier (and such not overloaded function), f1 would match rvalue (still §13.3.1):

p5 [...] non-static member functions declared without ref-qualifier, additional rule applies:

  • even if implicit object parameter not const-qualified, rvalue can bound parameter long in other respects argument can converted type of implicit object parameter.
struct test{   void f() { std::cout << "lvalue or rvalue object\n"; } };  int main(){   test t;   t.f(); // ok   test().f(); // ok } 

now, onto why @nicol's answer atleast partly wrong. says:

note declaration changes type of *this.

that wrong, *this always lvalue:

§5.3.1 [expr.unary.op] p1

the unary * operator performs indirection: expression applied shall pointer object type, or pointer function type and result lvalue referring object or function expression points.

§9.3.2 [class.this] p1

in body of non-static (9.3) member function, keyword this prvalue expression value address of object function called. type of this in member function of class x x*. [...]


Comments