2

I want to return a heavy object which I pass as a const reference without copying. The code looks something like this:

#include <iostream>

class heavy_obj {
    public:
        heavy_obj() {
            std::cout << "Standard" << std::endl;
        }

        heavy_obj(const heavy_obj&) {
            std::cout << "Copy" << std::endl;
        }

        heavy_obj(heavy_obj&& t) {
            std::cout << "Move" << std::endl;
        }

};

heavy_obj return_obj_or_default(const heavy_obj& t, bool ret) {
    if(ret) {
        return std::move(t); // Copy gets called here
    } else {
        return heavy_obj();
    }
}

int main()
{
    heavy_obj t {/* Something */};
    heavy_obj t2 = return_obj_or_default(t, true);
    heavy_obj t3 = return_obj_or_default(heavy_obj(/* Something Else */), true);
    /* Output:
       Standard
       Copy
       Standard
       Copy
    */
    return 0;
}

However, if I declare the move constructor as non-const as shown above the copy constructor will be called, which I do not want. I could remove the const in the function heavy_obj return_test(heavy_obj& t, bool ret) but then I am no longer able to pass rvalues as parameters.

How do I achieve my desired behavior? Marking the parameter to the move constructor as const: heavy_obj(const heavy_obj&& t)? Though it contradicts this?

Mike van Dyke
  • 2,724
  • 3
  • 16
  • 31
  • **then I am no longer able to pass rvalues as parameters** **Marking the parameter to the move constructor** ,why don't you use **return_obj_or_default(heavy_obj&& t,bool ret)** – HongluWong Aug 13 '21 at 09:54
  • @HongluWong Because I want to pass either a reference as `return_obj_or_default(t, true)` or a rvalue `return_obj_or_default(heavy_obj(), true)`, I could not pass a reference with `heavy_obj&& t` – Mike van Dyke Aug 13 '21 at 09:59
  • 2
    you should use **std::forward**,code like this ` template typename std::decay::type return_obj_or_default(T&& t,bool ret){ if( ret ) return std::forward(t); return typename std::decay::type{};` – HongluWong Aug 13 '21 at 10:27
  • Hmm, a lot of hacking for such a basic task, but at least it works with `move` – Mike van Dyke Aug 13 '21 at 10:51

1 Answers1

3

You can't move from a const value, so the function can't take by const &.

You could do this, so the caller has to supply an rvalue, either moving or explicitly copying lvalues they wish to pass.

heavy_obj return_obj_or_default(heavy_obj&& t, bool ret) {
    if(ret) {
        return std::move(t);
    } else {
        return heavy_obj();
    }
}

Or you could do this, so the caller doesn't have to do anything special, but it will implicitly copy lvalues.

heavy_obj return_obj_or_default(heavy_obj t, bool ret) {
    if(ret) {
        return std::move(t);
    } else {
        return heavy_obj();
    }
}
Caleth
  • 52,200
  • 2
  • 44
  • 75