3

Can I make the code below work without copying the object b?

#include <iostream>
using namespace std;
class A{
        public:
                A() = default;
                A(const A& a) { cout << "copy ctor\n"; }
                A(A&& a) { cout << "move ctor\n"; }
                A& operator=(const A& a) { cout << "copy\n"; return *this;}
                A& operator=(A&& a) { cout << "move\n";  return *this;}
};

A Gen() {
        A x;
        return x;
}

int main() {
        bool cached = true;
        const A b;
        const A& a = cached ? b : Gen();
}

It seems like when cached is true, then the copy ctor is called.

*Edit: in the real code, the class A is kinda big, so I want to avoid copying. *Edit 2: I made b as constant, to clarify the intent.

wk_j
  • 105
  • 6

2 Answers2

5

The overall type of your conditional operator is not a reference type, because the 'false' branch is not a reference. Thus, even in the 'true' branch, the b object has to be copied, in order to match the type of the Gen() call. The assignment makes a reference to that copied object.

In your case, you can avoid this copy operation by a simple cast of the object returned by Gen() in the 'false' branch, to a reference to that object:

const A& a = cached ? b : static_cast<const A&>(Gen());

Note that this cast is being performed (implicitly) in your original code, by the assignment operator (which, if cached is false will create a reference to the returned object).

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
4

When using the ternary operator, b is handled as glvalue. That will cause a copy constructor call. If you want to avoid that, write std::move(b) which provides you with rvalue reference. In that case move constructor will be called.

More info can be found here.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
Attis
  • 573
  • 1
  • 7
  • 19
  • But that would change the value `b`, doesn't it? – wk_j Apr 28 '21 at 11:34
  • 1
    @wk_j: `std::move` is just a cast, so no modification by itself. – Jarod42 Apr 28 '21 at 12:09
  • @Jarod42 I mean, eventually a move constructor is called, so that the object `b` changes. Oh, on second thought, I should have posted the question with `b` as const. – wk_j Apr 28 '21 at 12:13
  • 1
    @wk_j: Avoid to modify your question in a way which invalidate existing answer. Here, with the move, you got `const A&&` from `std::move(b)`, and `A` from `Gen()`, common type should be `const A`, so copy would be done. – Jarod42 Apr 28 '21 at 12:25