return type of operator+()

S

subramanian100in

Consider the following class( For simplicity I have not given the
member function definitions).

class Test
{
public:
explicit Test(int arg = -1);
Test(const Test& arg);
Test& operator=(const Test& rhs);
Test& operator+=(const Test& rhs);
int getValue(void) const;

private:
int value;
};

Suppose I define the overloaded operator+() as

inline const Test operator+(const Test& lhs, const Test& rhs)
{
Test temp = lhs;
temp += rhs;
return temp;
}

Consider the three instances of the class Test.
Test a(100);
Test b(200);
Test c(300);

I have kept the return type of operator+() as 'const Test' instead of
plain 'Test'. The reason is that if the return type were plain 'Test',
then it will allow the following expression:
(a + b) = c;

But we cannot write
(x + y) = z;
if x, y, z were built-in types. So, to mimic built-in types, the
overloaded operator+() has to return 'const Test' instead of plain
'Test' so that '(a+b) = c' will be disallowed by the compiler. Am I
correct ? Kindly advice me what is practiced regarding the return type
of operator+() in real-world applications?

Thanks
V.Subramanian
 
S

Scooser

Hi,
you cannot write (a + b) = c but also c = (a + b).someNonConstMethod()
is not possible any longer.

btw. why creating the temp object yourself? Let the compiler do this
for you.

inline const Test operator+ (Test lhs, const Test& rhs)
{
return lhs += rhs;
}
 
S

SG

you cannot write (a + b) = c but also c = (a + b).someNonConstMethod()
is not possible any longer.

Right. In C++0x a better approach would be to limit assignment to
lvalues instead of using const return values. This will make

(a+b)=c;

ill-formed but still allow

(a+b).someNonConstFunction()

if someNonConstFunction has no lvalue-ref-qualifier. Also, it allows
moving resources from (non-const) temporary objects to other objects
(move semantics). For small objects that only contain an integer this
won't matter. But in other cases like std::valarray it will make a
difference. An assignment operator restricted to lvalues will look
like this:

class Test {
...
public:
Test& operator=(Test const&) & ;
// note the ref qualifier: ^^^
...
};

In the light of C++0x, move constructors and ref qualifiers, the habit
of returning const objects is probably going to disappear. Even
without C++0x I don't return const objects. I just don't consider (a+b)
=c to be a problem.
btw. why creating the temp object yourself? Let the compiler do this
for you.

inline const Test operator+ (Test lhs, const Test& rhs)
{
   return lhs += rhs;
}

Before recommending something like this, please make sure that it is
actually an improvement over what the OP wrote. The problem here is
that no compiler I know of will be able to apply RVO (return value
optimization) in this case. For two reasons, actually. Firstly: It's a
parameter. Secondly: the return type of operator+= is an lvalue
reference. How would the compiler know what this reference refers to
without inlining the operator+= function? The compilers I tested are
not able to figure that out even with an inline operator+= and
optimizations enabled.

Assuming your compiler can apply URVO and NRVO except for parameters
(which is the behaviour of both Microsoft's and GNU's C++ compiler as
far as I know) and further assuming that your compiler can elide a
copy if the argument was an rvalue we get the following results:

1st arg | subramanian | Scooser
--------+-------------+--------
lvalue | 1 | 2
rvalue | 1 | 1

Table 1: Count of copy constructions

The one copy in subramanian's is the copy he created manually. NRVO
applies. RVO is not applied in Scooser's version at all. But in case
of rvalue arguments the copy can be elided which is why it also uses
only one copy construction. But for lvalue arguments two copy
constructions are used.

Cheers,
SG
 
V

Victor Bazarov

Consider the following class( For simplicity I have not given the
member function definitions).

class Test
{
public:
explicit Test(int arg = -1);
Test(const Test& arg);
Test& operator=(const Test& rhs);
Test& operator+=(const Test& rhs);
int getValue(void) const;

private:
int value;
};

Suppose I define the overloaded operator+() as

inline const Test operator+(const Test& lhs, const Test& rhs)
{
Test temp = lhs;
temp += rhs;
return temp;
}

Consider the three instances of the class Test.
Test a(100);
Test b(200);
Test c(300);

I have kept the return type of operator+() as 'const Test' instead of
plain 'Test'. The reason is that if the return type were plain 'Test',
then it will allow the following expression:
(a + b) = c;

So? What's the big deal?
But we cannot write
(x + y) = z;
if x, y, z were built-in types.

No. Neither can we call member functions for built-in types.
> So, to mimic built-in types, the
overloaded operator+() has to return 'const Test' instead of plain
'Test' so that '(a+b) = c' will be disallowed by the compiler. Am I
correct ?

Correct in what sense? If you are writing a template function that is
supposed to use binary operator+, and you expect to use that function
with both built-in types and some user-defined ones, it would never have
such a senseless code like (a+b)=c (because if you try, you can't use
that code with built-in types, as you said). If you're writing code to
exclusively use your type (and not built-in types), then what does it
matter whether your type mimics built-in types to a tee? Mimicking is
never exact, otherwise you'd have a copy. That's why you only implement
*part* of the functionality, the *important* part. The rest is fluff.
> Kindly advice me what is practiced regarding the return type
of operator+() in real-world applications?

Just an object. The fact that you can use a non-const member function
on the result of an expression and have it modify the temporary returned
before it's used in the rest of the expression is the added bonus that
does not exist in built-in types.

Bottom line: don't overthink it.

V
 
J

James Kanze

Consider the following class( For simplicity I have not given
the member function definitions).
class Test
{
public:
explicit Test(int arg = -1);
Test(const Test& arg);
Test& operator=(const Test& rhs);
Test& operator+=(const Test& rhs);
int getValue(void) const;
private:
int value;
};
Suppose I define the overloaded operator+() as
inline const Test operator+(const Test& lhs, const Test& rhs)
{
Test temp = lhs;
temp += rhs;
return temp;
}
Consider the three instances of the class Test.
Test a(100);
Test b(200);
Test c(300);
I have kept the return type of operator+() as 'const Test'
instead of plain 'Test'. The reason is that if the return type
were plain 'Test', then it will allow the following
expression:
(a + b) = c;
But we cannot write
(x + y) = z;
if x, y, z were built-in types. So, to mimic built-in types,
the overloaded operator+() has to return 'const Test' instead
of plain 'Test' so that '(a+b) = c' will be disallowed by the
compiler. Am I correct ?

Yes. Most people don't bother, but it is the preferred
solution, at least by some authors. (Scott Meyers, IIRC).
Kindly advice me what is practiced regarding the return type
of operator+() in real-world applications?

Follow the local coding guidelines:). Seriously, return a
value, not a reference; whether the value is const or not is
more a question of style---as you noted, const comes slightly
closer to emulating the built-in operator, but it's not a
familiar idiom, which may cause some readers to wonder, and the
mistakes it protects against aren't that frequent, so it may not
be worth the bother. (I decided a long time ago that my return
values from such operators should be const, but I've never
gotten around to actually modifying my existing code:).)
 
J

James Kanze

you cannot write (a + b) = c but also c = (a +
b).someNonConstMethod() is not possible any longer.

Which is, of course, the goal.
btw. why creating the temp object yourself? Let the compiler
do this for you.
inline const Test operator+ (Test lhs, const Test& rhs)
{
return lhs += rhs;
}

Maybe he likes readable code.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top