2

I am trying to do something similar to this:

#include <vector>
#include <memory>

struct Bar
    {
    Bar& doThings()
        {return *this;}

    std::unique_ptr<int> m_content; // A non-copyable type
    };

struct Foo
    {
    Foo& append(Bar&& obj)
        {
        objects.push_back(std::move(obj));
        return *this;
        }

    std::vector<Bar> objects;
    };

int test()
    {
    Foo test;
    test.append(std::move(Bar{}.doThings())) //Ok
    // Not ok
      .append(Bar{}.doThings())
        ;
    }

error: cannot bind rvalue reference of type Bar&& to lvalue of type Bar

Is it possible to make this work without the explicit std::move?

Trying to overload doThings does not solve the problem:

error: Bar&& Bar::doThings() && cannot be overloaded

user877329
  • 6,717
  • 8
  • 46
  • 88
  • 1
    The only way to get this to work without an explicit `move` is to return by value from `doThings`. – NathanOliver Jul 06 '18 at 19:10
  • @NathanOliver ... or append somthing else. – bipll Jul 06 '18 at 19:12
  • 2
    Yes, your code can be changed in dozens of ways to make it compile without a `std::move`. But the problem is your code, as written, doesn't compile. Without a description *outside of your code* saying *what you want it to do*, any changes will *change what your code does* with *zero guidance about what you want it to do*. Which in extreme means changing your code to `int main(){}` would make it compile. Sure, the program is different, but so is *any* change to your code. Why does `append` take `Bar&&`? Why does `doThings` return `Bar&`? Why call `doThings` at all? – Yakk - Adam Nevraumont Jul 06 '18 at 19:12
  • 1
    @Yakk-AdamNevraumont The code does compile, though. The code inside of `test` is there as an example, showing what the OP wants, so it doesn't have to compile. – Justin Jul 06 '18 at 19:26

2 Answers2

4

The problem is that when you return the instance from the function, you don't have an rvalue.

However, there is a way to overload functions based on the rvalue/lvalue of the object:

 Bar& doThings() & {return *this;}
 Bar doThings() && {return std::move(*this); }
R Sahu
  • 204,454
  • 14
  • 159
  • 270
JVApen
  • 11,008
  • 5
  • 31
  • 67
  • 2
    The downside here is that `doThings` becomes a "do something, then permit chaining" and becomes "do something, create an object, move into that object, destroy that new object". That could be mildly surprising; on the other hand, it is operating on an rvalue, and unless your move ctor is broken it shouldn't matter much. – Yakk - Adam Nevraumont Jul 06 '18 at 19:41
4

You can add ref-qualified overloads of doThings():

struct Bar
    {
    Bar& doThings() &
        {return *this;}

    Bar&& doThings() &&
        {return std::move(*this);}

    std::unique_ptr<int> m_content; // A non-copyable type
    };
Justin
  • 24,288
  • 12
  • 92
  • 142
  • Returning `&&` is dangerous and alnost always a bad idea; code that would usually reference lifetime extend temporaries doesn't work on `&&` return values. Of course, the alternative here is also bad, so... – Yakk - Adam Nevraumont Jul 06 '18 at 19:39
  • @Yakk-AdamNevraumont In this case, though, returning `&&` is the right thing. See [`std::optional::value`](https://en.cppreference.com/w/cpp/utility/optional/value), for example – Justin Jul 06 '18 at 19:42
  • @Justin Adding an extra overload is apparently ambiguous in this case – user877329 Jul 07 '18 at 05:20
  • @user877329 Notice how I ref-qualified both member functions. You have to ref-qualify every member function, not just the one `&&` overload you add – Justin Jul 07 '18 at 06:00