3

Given the following error:

class B {
private:
    int n;
public:
    B(int x) :
            n(x) {
    }
    B operator+(B& b) {
        return B(n + b.n);
    }
    friend ostream& operator<<(ostream &out, const B& b) {
        out << "B: " << b.n;
        return out;
    }
    bool operator<(const B& rhs) const {
        return n < rhs.n;
    }
};

int main() {
    B b1(2);
    B b2(3);
    B res121 = b1 + (b2 + b1); // ----error
    B res21 = b2 + b1;
    B res1_21 = b1 + res21; // ---- No error
    cout << res1_21;
    return 0;
}

Why I get error while I try to define res121 but don't get error while I try to define res1_21 ?
After all, b2+b1 is object in type of B , so what is the problem? What it's say that it's a temporary object , how can I know what is temporary object and what it's not.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Software_t
  • 576
  • 1
  • 4
  • 13
  • the result of `b2+b1` is a temporary object. It is called temporary, as it ceases to exist at the `;`. – Caleth Jul 09 '18 at 14:51
  • @Caleth So this is the only case of defining a temporary? – Software_t Jul 09 '18 at 14:52
  • It's the only case of trying to call a function accepting lvalue reference on a temporary. The initialisation of `res121`, `res21` and `res1_21` theoretically involve copying temporaries, except the copy doesn't happen – Caleth Jul 09 '18 at 14:54
  • @Caleth But I read in some sources that object that defined in scope is also a temporary object (so that if we will return it, if the function returns by-reference), so I don't understand it. – Software_t Jul 09 '18 at 14:55
  • That's a different meaning of the word temporary – Caleth Jul 09 '18 at 14:56
  • @Caleth So the intention in this context is an object that defined only in one line? – Software_t Jul 09 '18 at 14:57
  • You cant do this "return B(n + b.n);" because it returns an object that is only in scope inside the operator+ definition. – Katianie Jul 09 '18 at 15:01
  • @Katianie of course you can. You don't even need the `B(` ... `)` – Caleth Jul 09 '18 at 15:03
  • The C++ standard only uses "temporary" in the "result of an expression" sense. It refers to local variables of a function as "variables with automatic storage duration". Colloquially those are temporary because they cease to exist at the corresponding `}` of their definition – Caleth Jul 09 '18 at 15:08
  • related/dup: https://stackoverflow.com/questions/1565600/how-come-a-non-const-reference-cannot-bind-to-a-temporary-object – YSC Jul 09 '18 at 15:08
  • @Caleth no you cant....its declared on the stack and gets deallocated once the function ends, also the answer mentions this. – Katianie Jul 09 '18 at 15:11
  • @Katianie no, it's not *declared* at all. `B(n + b.n)` is an *expression* of type `B`, with the value-category pr-value. `return`ing it materialises it at the call site – Caleth Jul 09 '18 at 15:15
  • @Katianie and if it were different, how could *any* values be returned by functions? – Caleth Jul 09 '18 at 15:17
  • @Caleth You can return values for primitives but not for objects. It calls B(int) which is a constructor not an expression. The constructor then returns the memory location of the allocated memory and since it is not a pointer, that means the memory is allocated on the stack. – Katianie Jul 09 '18 at 15:44
  • @Katianie go read [`[expr.post]`](http://eel.is/c++draft/expr.post) and [`[stmt.return]`](http://eel.is/c++draft/stmt.return). You are looking at a *simple-type-specifier ( expression-list opt )* as the operand of `return` – Caleth Jul 09 '18 at 16:02
  • @Caleth Intresting, but in [stmt.return] it says "Note: A return statement can involve an invocation of a constructor to perform a copy or move of the operand if it is not a prvalue or if its type differs from the return type of the function. A copy operation associated with a return statement may be elided or converted to a move operation if an automatic storage duration variable is returned". Isen't B in this case an xvalue meaning it cannot be a prvalue? – Katianie Jul 09 '18 at 17:29
  • no, it is a prvalue. xvalues have an identity – Caleth Jul 09 '18 at 17:31

1 Answers1

10

A temporary object in C++ terminology is an object with no name or identity that comes into existence, usually as the return value of a function. How they work exactly changes between and .

Their existence ends at the end of the current full expression (usually a ;), and they cannot bind to non-const lvalue references, so B& cannot bind to a temporary.

This:

B operator+(B& b) {
    return B(n + b.n);
}

is a poor operator+. The best1 way to write operator+ is:

B& operator+=(B const& b)& {
  n += b.n;
  return *this;
}
friend B operator+(B lhs, B const& rhs) {
  lhs += rhs;
  return lhs;
}

where you implement += as a member function, then write a slightly asymmetric friend operator+ that is implemented in terms of +=.

The assymetry in + makes long chains of + expressions slightly more efficient, especially if you implement cheap-to-move objects.


1 Naturally there are situations where this isn't the best. Like sometimes real expression templates. But you should start with this and only get more complex if you prove you need it.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • @storyteller it lets you create a short inline ADL forwarder that supports implicit typr conversions on both left and right. You coukd put it outside thr class instrad, but given kts 2 line body... plus, if thr class is a template, this pattern works better thsn mosg alternatives, as it generates a non trmplate operator + .for each template instance. – Yakk - Adam Nevraumont Jul 10 '18 at 05:25