Is a constant modifier needed here?

Effective C ++ in paragraph 3 says: β€œUse const whenever possible,” and it gives an example like:

const Rational operator*(const Rational& lhs, const Rational& rhs); 

so that customers cannot commit atrocities like this:

 Rational a, b, c; ... (a * b) = c; // invoke operator= on the result of a*b! 

But isn't the reference return value of allready a rvalue functions? So why do this?

+44
c ++ coding-style c ++ 11 const rvalue
May 30 '13 at 11:26
source share
5 answers

The fact is that for class types (but not for built-in types) a = b is short for a.operator=(b) , where operator = is a member function. And member functions can be called on r values, such (a * b) created by Rational::operator* . To apply similar semantics as for built-in rvalues ​​(" do as ints do "), some authors (including Meyers) recommended in C ++ 98 to return const-rvalue for classes with such operators.

However, in C ++ 11, returning const-rvalue is a bad idea, because it will interfere with the semantics of movement, because the rvalues ​​constants cannot bind to T&& .

In his notes Review of the New C ++ (C ++ 11) , Scott Meyers gives exactly the same example from his old book, and concludes that poor design is now considered to add a const value. Recommended Signature Now

 Rational operator*(const Rational& lhs, const Rational& rhs); 

UPDATE: As seen from @ JohannesSchaub-litb's comments, in C ++ 11 you can also use the assignment operator reference specifier so that it only accepts lvalues ​​as its left argument (i.e. the *this pointer, so this the function is also known as "rvalue links for this"). You will need g ++> = 4.8.1 (just released) or Clang> = 2.9 to use it.

+50
May 30 '13 at 11:30
source share

A constant modifier of the return value is not necessary and may interfere with the semantics of movement. The preferred way to prevent rvalues ​​from being assigned in C ++ 11 is to use ref-qualifiers .

 struct Rational { Rational & operator=( Rational other ) &; // can only be called on lvalues }; Rational operator*( Rational const & lhs, Rational const & rhs ); Rational a, b, c; (a * b) = c; // error 
+6
Jun 01 '13 at 1:22
source share

Maybe it will cost me reputation points, but I do not agree. Do not change the expected return types of overloaded operators, as this will annoy the users of your class. those. use

 Rational operator*(const Rational& lhs, const Rational& rhs); 

(Of course, const parameters are good practice, and having constant reference parameters is even better, since this means that the compiler will not take deep copies. But in this case, it is not necessary to have the return value of the link since you will get a dead link that is disastrous But note that sometimes, accepting a link is slower than skipping by value. I think double and int fall into this category on many platforms.)

+5
May 30 '13 at 11:30
source share

Because you can write (a * b) == c instead of ie

 if ((a * b) = (c + d)) // executes if c+d is true 

But you wanted

 if ((a * b) == (c + d)) // executes if they're equal 
+3
May 30 '13 at 11:29
source share

I assume that you would like to do according to your question: declare the corresponding operator = private so that it is no longer available.

so you want to overload the signature that matches (a*b) = c . I agree that the left side is an expression, and therefore the value of r will be better. however, you ignore the fact that this is the return value of the function if you overload the function to return an r value that the compiler will complain about invalid overloads, since overload rules do not take into account the return values.

As stated here , operator overloading for assignment is always an internal class definition. if there was an odd signature like void operator=(foo assignee, const foo& assigner); overload resolution might match the first part as an rvalue (then you could remove it or declare it closed).

So, you can choose one of two worlds:

  • live with the fact that users can write stupid things, such as (a*b) = c , which is not so, but saves the value of c in an inaccessible temporary
  • use the signature const foo operator*(const foo& lhs, const foo& rhs) , which does not allow semantics (a*b) = c and the victim of the move

the code

 #include <utility> class foo { public: foo() = default; foo(const foo& f) = default; foo operator*(const foo& rhs) const { foo tmp; return std::move(tmp); } foo operator=(const foo& op) const { return op; } private: // doesn't compile because overloading doesn't consider return values. // conflicts with foo operator=(const foo& op) const; foo && operator=(const foo& op) const; }; int main ( int argc, char **argv ) { foo t2,t1; foo t3 = t2*t1; foo t4; (t2 * t1) = t4; return 0; } 
+1
May 30 '13 at 20:31
source share



All Articles