Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does operator ++ return a non-const value?

Tags:

I have read Effective C++ 3rd Edition written by Scott Meyers.

Item 3 of the book, "Use const whenever possible", says if we want to prevent rvalues from being assigned to function's return value accidentally, the return type should be const.

For example, the increment function for iterator:

const iterator iterator::operator++(int) {     ... } 

Then, some accidents is prevented.

iterator it;   // error in the following, same as primitive pointer // I wanted to compare iterators if (it++ = iterator()) {   ... } 

However, iterators such as std::vector::iterator in GCC don't return const values.

vector<int> v; v.begin()++ = v.begin();   // pass compiler check 

Are there some reasons for this?

like image 817
skymountain Avatar asked Jun 10 '11 11:06

skymountain


People also ask

Why does the assignment operator return a reference?

Strictly speaking, the result of a copy assignment operator doesn't need to return a reference, though to mimic the default behavior the C++ compiler uses, it should return a non-const reference to the object that is assigned to (an implicitly generated copy assignment operator will return a non-const reference - C++03 ...

What does the operator return?

The assignment operators return the value of the object specified by the left operand after the assignment. The resultant type is the type of the left operand. The result of an assignment expression is always an l-value.

Why can't you make a non const reference to a literal?

A non-const reference cannot point to a literal. You cannot bind a literal to a reference to non-const (because modifying the value of a literal is not an operation that makes sense) and only l-values can be bound to references to non-const.

Can a const function return a non const reference?

If the thing you are returning by reference is logically part of your this object, independent of whether it is physically embedded within your this object, then a const method needs to return by const reference or by value, but not by non-const reference.


2 Answers

I'm pretty sure that this is because it would play havoc with rvalue references and any sort of decltype. Even though these features were not in C++03, they have been known to be coming.

More importantly, I don't believe that any Standard function returns const rvalues, it's probably something that wasn't considered until after the Standard was published. In addition, const rvalues are generally not considered to be the Right Thing To Do™. Not all uses of non-const member functions are invalid, and returning const rvalues is blanketly preventing them.

For example,

auto it = ++vec.begin(); 

is perfectly valid, and indeed, valid semantics, if not exactly desirable. Consider my class that offers method chains.

class ILikeMethodChains { public:     int i;     ILikeMethodChains& SetSomeInt(int param) {         i = param;         return *this;     } }; ILikeMethodChains func() { ... } ILikeMethodChains var = func().SetSomeInt(1); 

Should that be disallowed just because maybe, sometimes, we might call a function that doesn't make sense? No, of course not. Or how about "swaptimization"?

std::string func() { return "Hello World!"; } std::string s; func().swap(s); 

This would be illegal if func() produced a const expression - but it's perfectly valid and indeed, assuming that std::string's implementation does not allocate any memory in the default constructor, both fast and legible/readable.

What you should realize is that the C++03 rvalue/lvalue rules frankly just don't make sense. They are, effectively, only part-baked, and the minimum required to disallow some blatant wrongs whilst allowing some possible rights. The C++0x rvalue rules are much saner and much more complete.

like image 77
Puppy Avatar answered Sep 27 '22 21:09

Puppy


If it is non-const, I expect *(++it) to give me mutable access to the thing it represents.

However, dereferencing a const iterator yields only non-mutable access to the thing it represents. [edit: no, this is wrong too. I really give up now!]

This is the only reason I can think of.

As you rightly point out, the following is ill-formed because ++ on a primitive yields an rvalue (which can't be on the LHS):

int* p = 0; (p++)++; 

So there does seem to be something of an inconsistency in the language here.

like image 26
Lightness Races in Orbit Avatar answered Sep 27 '22 23:09

Lightness Races in Orbit