Search code examples
c++inheritanceclangwarningsmove

Reasoning behind: local variable 'derived' will be copied despite being returned by name


This is a follow up to Is -Wreturn-std-move clang warning correct in case of objects in the same hierarchy?, because I don't understand reasoning in certain specific cases.

struct Field {
  Field();
//  Field(const Field&); // (a)
  int a[1000000];
};

struct Base
{
  Base();
//  Base(const Base&); // (b)
//  Base(Base&&); // (c)
  Field f;
};

struct Derived : Base { };

Base foo()
{
  Derived derived;
  return derived;
}
  1. The above code compiles just fine. I understand, std::move wouldn't do anything different.
  2. Uncomment (a), you get the warning. Why? I believe std::move still won't do anything different.
  3. Uncomment (b) (keep (a) uncommented), works fine again. How is it any different than #2?
  4. Uncomment (c) (keep (a) and (b) uncommented), shows warning again. This makes sense, std::move could save us a copy.
  5. Have foo return Derived, works fine again. How is it any different than #4?

Solution

  • As state in link answer, and in cppreference

    We first consider derived as rvalue, and if it doesn't select XXX(/*const*/Derived&&) (until C++20) then we reconsider it as lvalue.

    So as long as there are no Base(Derived&&), a copy is done.

    I didn't find the whole logic of the warning though., 1. 2. 3. should be treated the same IMO (either no warnings as currently equivalent, or warning for future proof).

    For 1.

    copy and move constructor are generated by compiler.
    copy or move give same result at the end.

    For 2.

    Field move constructor is non longer generated, making the whole chain copy instead of move.
    copy or move give same result at the end.

    For 3.

    Base move constructor is non longer generated, making copy from the start.
    copy or move give same result at the end.

    For 4.

    Base has both copy and move constructor.
    copy or move give different result.

    For 5.

    We now have match XXX(Derived&&) (XXX=Derieved), so no copy done.