Почему myClassObj++++ не вызывает ошибку компиляции: '++' нуждается в l-значении так же, как и встроенный тип?

Почему myint++++ отлично компилируется с компилятором VS2008 и компилятором gcc 3.42?? Я ожидал, что компилятор скажет, что нужно lvalue, пример см. Ниже.

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 ??
}

person Gob00st    schedule 14.07.2011    source источник
comment
Компилятор также не замечает, что ++++i; неверно.   -  person MSalters    schedule 14.07.2011
comment
@MSalters: ++++i действительно неправильно?   -  person Oliver Charlesworth    schedule 14.07.2011
comment
Да. Изменение i дважды без промежуточной точки последовательности — это неправильно.   -  person MSalters    schedule 14.07.2011
comment
@MSalters: Ах, да, точки последовательности. Я думал только о значениях lvalue и rvalues.   -  person Oliver Charlesworth    schedule 14.07.2011
comment
@Oli, @MSalters: ++++i правильно сформирован, но у него есть UB. Я не думаю, что компилятор ошибся, приняв это :)   -  person Armen Tsirunyan    schedule 14.07.2011
comment
вместо этого верните const MyInt, он достигает того, чего вы хотите, и является лучшей практикой, поскольку пользователям сложно сделать что-то «не так» с вашим классом. См. также главу «Эффективный C++» о возврате константных объектов из оператора +.   -  person stijn    schedule 14.07.2011
comment
@MSalters: как это изменить дважды без промежуточных точек последовательности? Это касается только таких выражений, как ++i - ++i, где operator- имеет два аргумента и не определено, какой (или, действительно, если есть) один из них оценивается первым. Но в ++++i есть только один аргумент ++i для внешнего operator++, который должен быть полностью вычислен первым, чтобы внешний operator++ запустился. Оно не более неопределенно, чем std::cout<<"1st"<<"2nd". (Это не обязательно относится к внутренним типам, где operator++ не обязательно является реальной функцией.)   -  person leftaroundabout    schedule 14.07.2011
comment
@leftaroundabout: Вы говорите о порядке оценки. Другое дело (и OoE не указано, а не не определено). Проблема в том, что ++(++i) действительно сначала оценивает внутренний (++i), но затем - без точки следования - снова изменяет тот же i. Итак, мы знаем конкретный порядок оценки, и мы знаем, что это необходимо неправильно.   -  person MSalters    schedule 14.07.2011
comment
Еще один глупый вопрос, который никогда не возникнет в реальном коде. Мало того, что нечитабельно. Это неопределенное поведение.   -  person Martin York    schedule 14.07.2011
comment
@Martin: Вызов ++++ для объекта типа класса не только правильно сформирован, но и имеет четко определенное поведение. Поскольку ++ в этом случае является вызовом функции, он вводит точку следования   -  person Armen Tsirunyan    schedule 14.07.2011
comment
@Armen Tsirunyan: Ты серьезно собираешься спорить с этим (быть тупым только для того, чтобы высказать мнение, утомительно). Как вы думаете, вы когда-нибудь увидите такой код в дикой природе! Как вы думаете, какой-нибудь другой инженер позволил бы вам проверить это в системе контроля версий? Тот факт, что он хорошо определен для классов, ни здесь, ни там. Проблема в том, что он плохо определен для POD. Этот факт делает обслуживание кошмаром. Так как функция, которую вы написали год назад, теперь наивно преобразована в шаблон, и кто-то начинает использовать ее с целыми числами (BANG вот и все, что вы определили).   -  person Martin York    schedule 14.07.2011
comment
@Martin: я не утверждаю, что писать что-то подобное в производственном коде — это нормально. Но я настаиваю на том, что он хорошо определен для объектов типа класса, в том числе для POD-структур. С чего вы взяли, что это не очень хорошо определено для POD-структур?   -  person Armen Tsirunyan    schedule 14.07.2011
comment
@MSalters: здесь есть точка последовательности после завершения оценки внутреннего operator++ и до ввода внешнего operator++. Это то, что я пытался сделать.   -  person leftaroundabout    schedule 14.07.2011
comment
@Армен Цирунян: Повторение одного и того же, как только я объяснил, почему это не имеет значения, не делает его корректором. Он не определен для всех POD.   -  person Martin York    schedule 14.07.2011
comment
@leftaroundabout: извините, нет. operator++ для встроенных типов не является вызовом функции, поэтому перед ним или после него нет точки следования. @Martin: Армен прав: если это класс POD, ++ - это вызов функции с точками последовательности до и после.   -  person MSalters    schedule 15.07.2011


Ответы (6)


Нет, перегруженные операторы — это не операторы, а функции. Таким образом, GCC правильно принимает это.

myobj++++; эквивалентно myobj.operator++(0).operator++(0); Разрешен вызов функции-члена (включая перегруженный оператор) для временного объекта типа класса.

person Armen Tsirunyan    schedule 14.07.2011
comment
Это неверно: перегруженные операторы являются и операторами и функциями. Важным моментом является то, что требования к определяемым пользователем операторам относятся к реализации оператора, которая может различаться в зависимости от того, как вы его реализуете. В данном конкретном случае в качестве функции-члена. - person David Rodríguez - dribeas; 14.07.2011

Потому что для пользовательских типов перегрузки операторов — это просто вызовы функций, и поэтому они подчиняются семантике вызовов функций.

person Oliver Charlesworth    schedule 14.07.2011

Если вы хотите эмулировать встроенное поведение, на самом деле есть очень простое решение: сделайте возвращаемое значение const:

MyInt const operator++(int) { … }

Несколько лет назад велись споры о том, должны ли определяемые пользователем операторы точно моделировать встроенное поведение. Я не уверен, какая школа мысли в настоящее время имеет преимущество, но создание возвращаемого типа operator++(int) const было способом добиться этого.

person Konrad Rudolph    schedule 14.07.2011

В конце концов, MyInt::operator++(int) — это просто еще один метод. Применяются те же правила. Поскольку вы можете вызывать методы для rvalue, вы можете вызывать operator++(int) для rvalue.

person MSalters    schedule 14.07.2011

myint++ возвращает что-то похожее на MyInt(2). Итак, это похоже на выполнение MyInt(2)++. В функции operator++ создается временный класс, и вы увеличиваете временный класс. После того, как он возвращается, он удаляется, как только заканчивается следующий оператор (здесь это второй оператор ++).

person holgac    schedule 14.07.2011

Проблема в том, что требования оператора постинкремента для целочисленных типов и для пользовательских типов различаются. В частности, определяемый пользователем постинкрементный оператор, реализованный как функция-член, позволяет использовать rvalue.

Если вы реализовали оператор как бесплатную функцию:

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

Тогда требования этого конкретного оператора будут извлечены из фактической подписи. Если первый аргумент принимается по значению, то он принимает rvalue напрямую, если он принимает аргумент через const &, то он принимает rvalue, если конструктор копирования доступен, если аргумент принимается неконстантным &, тогда этому оператору потребуются lvalues.

person David Rodríguez - dribeas    schedule 14.07.2011