3

In the below code:

class Myclass
{    
public:
    Myclass() = default;
    ~Myclass() = default;

    Myclass(Myclass&&) = default;
    Myclass& operator=(Myclass&&) = default;

    Myclass(const Myclass&) = delete;    
    Myclass& operator=(const Myclass&) = delete;
};

Myclass GetObj()
{
    Myclass obj;    
    return obj;
}

Myclass WrapperOfGetObj()
{
    Myclass&& Wrapobj = GetObj();

    return Wrapobj;
}

int main()
{
    return 0;
}

When it is compiled it gives error in function WrapperOfGetObj on return Wrapobj. Error is

'Myclass::Myclass(const Myclass &)': attempting to reference a deleted function

It means it is trying to call deleted copy constructor. As I know Wrapobj is lvalue and when it being returned its behavior should be same as obj in GetObj method i.e, calling move constructor at the time of return. Then why it is looking for copy constructor? What am I missing here?

Ajay
  • 18,086
  • 12
  • 59
  • 105
gaurav bharadwaj
  • 1,669
  • 1
  • 12
  • 29

1 Answers1

6

The problem here is that (as T.C. wraps up in the comment section) Wrapobj is a reference, not an object, therefore implicit moving - i.e. treating the returned lvalue as an rvalue - does not apply in this case (see [class.copy]/32).

You could fix this by writing:

Myclass Wrapobj = GetObj();

Instead of the current:

Myclass&& Wrapobj = GetObj();

Or by explicitly std::move()ing Wrapobj when returning it. I'd personally advise to go for the first option.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • [this](http://stackoverflow.com/questions/1116641/is-returning-by-rvalue-reference-more-efficient) might be good extra reading for this topic. – krzaq Oct 14 '16 at 08:00
  • 1
    The expression `Wrapobj` is an lvalue of type `MyClass`; expressions never have reference type (after the [expr]/5 adjustment). The problem is that the implicit move only applies to objects of automatic storage duration ([class.copy]/32), and a reference is not an object. Moreover, "named lvalues in a return statement won't be treated as rvalues by the compiler if their type is different from the return type of the function" is incorrect since [CWG 1579](http://wg21.link/CWG1579). – T.C. Oct 14 '16 at 08:04
  • @T.C.: Has that been clarified in the Standard? Back in the day when I was more active I used to be convinced of that, but then I figured it was probably up to interpretation (see [Scott's blog post](http://scottmeyers.blogspot.cz/2015/02/expressions-can-have-reference-type.html)). Is there a final word? Anyway I'll try to reword it more precisely. – Andy Prowl Oct 14 '16 at 08:07
  • I don't think it's very useful to consider the pre-adjustment type when it vanishes instantly (hence the parenthesized qualifier). – T.C. Oct 14 '16 at 08:12
  • @T.C.: Re that CWG issue, the current wording of [class.copy]/32 says "When the criteria for elision of a copy/move operation are met [...] and the object to be copied is designated by an lvalue, [...]", and the criteria for elision ([class.copy]/31, first bullet) specify that the type has to be the same (ignoring cv-qualifiers) in order for elision to be possible. – Andy Prowl Oct 14 '16 at 08:18
  • ... "or when the expression in a `return` statement is a (possibly parenthesized) *id-expression* that names an object with automatic storage duration declared in the body or *parameter-declaration-clause* of the innermost enclosing function [...]". It's `(copy elision criteria met && lvalue) || (id-expression designating an automatic object)` This sentence is a tricky parse, I know... – T.C. Oct 14 '16 at 08:23
  • @T.C.: Yeah, you're right, I guess I parsed it the wrong way. However, I'm confused. Why is [this](http://melpon.org/wandbox/permlink/UuvVd79Q87eaz6PQ) illegal then? Both Clang and GCC reject it – Andy Prowl Oct 14 '16 at 08:30
  • @AndyProwl did you get answer why you program gets same error... I am confused. can you say few word on that. – gaurav bharadwaj Oct 14 '16 at 10:27
  • @gauravbharadwaj I'm trying to figure that out. I asked another question on Stack Overflow. Anyway the solution/workaround I would suggest you for the moment is to `return std::move(Wrapobj)`. – Andy Prowl Oct 14 '16 at 10:48
  • 1
    @T.C.: I wrote a [separate question](http://stackoverflow.com/questions/40039379/why-is-move-constructor-not-picked-when-returning-a-local-object-of-type-derived?noredirect=1#comment67358107_40039379) – Andy Prowl Oct 14 '16 at 10:48
  • @T.C.: Thanks for the answer. Edited. Sorry it took me a while to figure it all out. – Andy Prowl Oct 14 '16 at 11:35
  • @AndyProwl one question if in WrapperOfGetObj() function - I do WrapObj.SomeMeberVariable = assignSomeValue. Is that fine? as WrapObj is an xvalue. – gaurav bharadwaj Nov 06 '16 at 15:12