2
class outer
{
  class inner
  {
  public:
    inner() { }
    inner(inner&& rhs);
  }
public:
  outer() { }
  outer(outer&& rhs)
   : m_inner(rhs.m_inner)  // why is rhs.m_inner an lvalue and not an rvalue?
  { }
private:
  inner m_inner;
};

Why is rhs.m_inner an lvalue in this context? If rhs is an rvalue, why is rhs.m_inner not also an rvalue?

Mordachai
  • 9,412
  • 6
  • 60
  • 112
  • 1
    There seem to be two separate questions here. The first is "Why is the move constructor not used unless I type `std::move`?". The second is "Why does my class have a copy constructor?". They are separate questions, not really related, and it would probably be better if you could edit your question to focus on only one of them. –  Dec 08 '14 at 20:24
  • 3
    For your first question: [in C++11 why use std::move when you have &&](http://stackoverflow.com/q/14486367) – AliciaBytes Dec 08 '14 at 20:26
  • 1
    For your second question it's already answered at [What are all the member-functions created by compiler for a class? Does that happen all the time?](http://stackoverflow.com/q/3734247) basically having a copy constructor disables the implicit move constructor but not the other way around. – AliciaBytes Dec 08 '14 at 20:30
  • Okay, yes, that link does explain the answer. This could be closed as yet-another-duplicate :) – Mordachai Dec 08 '14 at 20:46
  • 1
    @Mordachai it's one of the most confusing topics relating to `move`. That said the awesome [What is move semantics?](http://stackoverflow.com/a/11540204) answers it perfectly under the section `Moving into members`. As awesome as the question is it's horrible at the same time again since it should probably be split up, it's too comprehensive and you can easily overlook the specific part you're looking for. Over and out. – AliciaBytes Dec 08 '14 at 20:52

3 Answers3

2

The source text rhs refers to two different things here:

  • The variable rhs, an rvalue-reference to an outer object. This variable has type outer&&.
  • The expression rhs that evaluates the value of that variable. This expression has type outer and value category lvalue.

The lvalue/rvalue-ness of a reference variable determines to which value categories of expressions the reference can bind, it has no affect on the value category of expressions that evaluate the variable. Evaluating a reference always results in an lvalue.

Casey
  • 41,449
  • 7
  • 95
  • 125
0

If rhs was an rvalue, then rhs.m_inner would be an rvalue too. However, rhs is a named variable (doesn't matter that it's an rvalue reference), so it's an lvalue.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
0

With reference to C++ Annotations, to call outer(outer&& rhs), one would use the code:

outer out((outer)outer()/*0*/)/*1*/;

Which at 0 constructs an anonymous object of type outer, and at 1 it constructs an object of type outer with the name out. (The (outer) is there to prevent out form being an outer(outer (*)()))

See the anonymous there? Now consider the definition of outer(outer&& rhs):

outer(outer && rhs) : m_inner(rhs.m_inner) {}

outer(outer &&) has 1 argument of type outer && named rhs. Now, there is a perceivable difference between the statement 0 in ex. 1 and the first parameter: the former doesn't have a name, the latter does, hence removing the anonimity of the object. Now since the object has a name it is just a lvalue reference (which allows people to use a construct-by-swap idiom).
The problem arises, though, when someone needs to use it as a rvalue reference. For that the

template<class T>
typename std::remove_reference<T>::type&& move(T&& t);

function was created. What's it do? It "obtains an rvalue reference to its argument and converts it to an xvalue" (more on all sorts of values here), which is exactly what we need! So, the final code could be:

#include <utility>

class outer {
  class inner {
    public:
      inner() {}
      inner(inner && rhs) {}
  };

  public:
    outer() {}
    outer(outer && rhs) : m_inner(std::move(rhs.m_inner)) {}

  private:
    inner m_inner;
};

Note the #include <utility> and std::move() call.

набиячлэвэли
  • 4,099
  • 4
  • 29
  • 40