8

Why myint++++ compiles fine with VS2008 compiler and gcc 3.42 compiler ?? I was expecting compiler say need lvalue, example see below.

struct MyInt
{
    MyInt(int i):m_i(i){}

    MyInt& operator++() //return reference,  return a lvalue
    {
        m_i += 1;
        return *this;
    }

    //operator++ need it's operand to be a modifiable lvalue
    MyInt operator++(int)//return a copy,  return a rvalue
    {
        MyInt tem(*this);
        ++(*this);
        return tem;
    }

    int     m_i;
};

int main()
{
    //control: the buildin type int
    int i(0);
    ++++i;  //compile ok
    //i++++; //compile error :'++' needs l-value, this is expected

    //compare 
    MyInt  myint(1);
    ++++myint;//compile ok
    myint++++;//expecting compiler say need lvalue , but compiled fine !? why ??
}
RoundPi
  • 5,819
  • 7
  • 49
  • 75
  • The compiler also fails to notice that `++++i;` is wrong. – MSalters Jul 14 '11 at 10:54
  • @MSalters: Is `++++i` actually wrong? – Oliver Charlesworth Jul 14 '11 at 10:56
  • 1
    Yes. Modifying `i` twice without an intervening sequence point, that's wrong. – MSalters Jul 14 '11 at 10:57
  • @MSalters: Ah, yes, sequence points. I was thinking only of lvalues and rvalues. – Oliver Charlesworth Jul 14 '11 at 10:58
  • 1
    @Oli, @MSalters: ++++i is well-formed, but it has UB. I don't think the compiler is wrong to accept it :) – Armen Tsirunyan Jul 14 '11 at 11:03
  • 2
    return a const MyInt instead, it achieves what you want and is better practice as it makes it hard for users to do something 'wrong' with your class. See Effective C++ chapter on returning const objects from operator + as well. – stijn Jul 14 '11 at 11:05
  • 2
    @MSalters: How is this modifying twice without intervening sequence points? That's only a concern in an expressions like `++i - ++i`, where `operator-` has two arguments and it's undefined which (or, indeed, _if any_) one of these is evaluated first. But in `++++i`, there is just one argument `++i` for the outer `operator++`, which _must_ be fully evaluated first for the outer `operator++` to start. It is no more undefined than `std::cout<<"1st"<<"2nd"`. (This just does not necessarily apply for internal types, where `operator++` is not guaranteed to be an actual function.) – leftaroundabout Jul 14 '11 at 11:25
  • @leftaroundabout: You're talking about order of evaluation. That's another matter (and OoE is _unspecified_, not _undefined_). The problem is that `++(++i)` does indeed evaluate the inner `(++i)` first, but then - without sequence point - goes on to modify the same `i` again. So we know the specific order of evaluation, and we know that it is necessary wrong. – MSalters Jul 14 '11 at 11:33
  • 1
    Another silly question that would never happen in real code. Not only is it unreadable. It is undefined behavior. – Martin York Jul 14 '11 at 11:46
  • @Martin: Calling ++++ on an object of class type is not only well-formed, but has well-defined behavior. Since ++ in that case is a function call, it introduces a sequence point – Armen Tsirunyan Jul 14 '11 at 11:57
  • @Armen Tsirunyan: Are you seriously going to argue that point (being obtuse just to make a point is tiresome). Do you think you will ever see code like that in the wild! Do you think any other engineer would let you get away with checking that into source control. The fact that it is well defined for classes is neither here nor there. The problem is that it is not well defined for POD. This fact makes it a maintenance nightmare. Since the function you wrote one year ago is now naively transformed into a template and somebody start using it with integers (**BANG** there goes your well defined). – Martin York Jul 14 '11 at 12:12
  • @Martin: I am not arguing that it's OK to write something like that in production code. But I do insist it's well defined for objects of class type, including for POD-structs. Where did you get the idea that it's not well-defined for POD-structs? – Armen Tsirunyan Jul 14 '11 at 12:16
  • 1
    @MSalters: there _is_ a sequence point here, after evaluation of the inner `operator++` is finished and before the outer `operator++` is entered. That is the point I was trying to make. – leftaroundabout Jul 14 '11 at 12:56
  • @Armen Tsirunyan: Repeating the same thing once I explained why it is irrelevant does not make it any corrector. It is not well defined for all POD. – Martin York Jul 14 '11 at 17:52
  • @leftaroundabout: Sorry, no. `operator++` for built-in types is not a function call, and therefore there's no sequence point before or after it. @Martin: Armen's right: if it's a POD class, `++` is a function call with sequence points before and after. – MSalters Jul 15 '11 at 07:25

6 Answers6

8

No, overloaded operators are not operators - they're functions. So GCC is correct to accept that.

myobj++++; is equivalent to myobj.operator++(0).operator++(0); Caling a member function (including overloaded operator) on a temprorary object of class type is allowed.

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • 2
    This is not correct: overloaded operators are both operators **and** functions. The important bit is that the *requirements* for user defined operators are those of the implementation of the operator, which might differ depending on *how* you implement it. In this particular case as a member function. – David Rodríguez - dribeas Jul 14 '11 at 11:38
3

Because for user-defined types, operator overloads are literally just function calls, and so obey the semantics of function calls.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
2

If you want to emulate the built-in behaviour there’s actually a very simple solution: make the return value const:

MyInt const operator++(int) { … }

A few years back there was a debate over whether user-defined operators should exactly model the built-in behaviour. I’m not sure which school of thought currently has the upper hand, but making the return type of operator++(int) const was a way of achieving this.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
1

In the end, MyInt::operator++(int) is just another method. The same rules apply. Since you can call methods on rvalues, you can call operator++(int) on rvalues.

MSalters
  • 173,980
  • 10
  • 155
  • 350
1

myint++ returns something similar to MyInt(2). So, it's similar to doing MyInt(2)++. A temporary class is created in the operator++ function and you're incrementing the temporary class. After it's returned, it's deleted as soon as the next statement finishes (here, it's second ++ operator).

holgac
  • 1,509
  • 1
  • 13
  • 25
1

The issue is that the requirements of the postincrement operator for integral types and for user defined types are different. In particular a user defined postincrement operator implemented as a member function allow the use of rvalues.

If you had implemented the operator as a free function:

MyInt operator++(MyInt [const][&] x, int)

Then the requirements of that particular operator would be those extracted from the actual signature. If the first argument is taken by value, then it accepts rvalues directly, if it takes the argument by const & then it accepts rvalues if the copy constructor is accessible, if the argument is taken by non constant & then that operator will require lvalues.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489