Постфиксный инкремент для пользовательского типа.
От: BugZ Россия  
Дата: 09.04.07 10:40
Оценка:
Добрый день!

Объясните, пожалуйста, почему выполняется my_int++ = ++my_int; в приведённом ниже примере.
Для встроенных типов постфиксный инкремент возвращает r-value.
Тут, насколько я понимаю, после выполнения постфиксного ++ создается временная копия объекта, которая будет разрушена после ; (выход из scope).
И она l-value (?).
Распишите, пожалуйста механизмы происходящего. Запутался.



#include <iostream>

class MyInt {
 public: MyInt( const MyInt& r ): i(r.i) {
  std::cout << "MyInt(copy)" << endl; }
 public: MyInt( int initial_i ): i(initial_i) {
  std::cout << "MyInt(int)" << endl; }
 
 // =
 MyInt& operator=(const MyInt& r){
  std::cout << "MyInt(=)" << endl; 
  i = r.i; 
  return *this;
 }
 // prefix
 MyInt& operator ++ () {
  ++i;
  return *this; 
 }
 // postfix
 MyInt operator ++ ( int fake ) {
  MyInt old_value( *this );
  ++i;
  return old_value; 
 }
 
 int i;
};
 
int main(int argc, char* argv[])
{
 MyInt my_int( 0 );
 my_int++ = ++my_int;
 cout << "my_int=" << my_int.i << endl;
 
 char c;
 cin >> c;
 
 return 0;
}
Re: Постфиксный инкремент для пользовательского типа.
От: _nn_  
Дата: 09.04.07 10:48
Оценка:
Здравствуйте, BugZ, Вы писали:

BZ>

<skip>

BZ> // postfix
BZ> MyInt const operator ++ ( int fake ) {
BZ>  MyInt old_value( *this );
BZ>  ++i;
BZ>  return old_value; 
BZ> }
 
<skip>
BZ>


А так ?
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Постфиксный инкремент для пользовательского типа.
От: Константин Л.  
Дата: 09.04.07 10:51
Оценка: -1
Здравствуйте, BugZ, Вы писали:


BZ>

[]

//copy ctor и operator = совчем не обяз. определять. Дефолтные сойдут
 
BZ>int main(int argc, char* argv[])
BZ>{
BZ> MyInt my_int( 0 );
BZ> my_int++ = ++my_int; //UB 
BZ> cout << "my_int=" << my_int.i << endl;
 
BZ> char c;
BZ> cin >> c;
 
BZ> return 0;
BZ>}
BZ>
Re[2]: Постфиксный инкремент для пользовательского типа.
От: BugZ Россия  
Дата: 09.04.07 11:06
Оценка:
Здравствуйте, _nn_, Вы писали:

__>А так ?


А так, понятное дело, всё хорошо
Мне интересно понять, что происходит без const.
Re[2]: Постфиксный инкремент для пользовательского типа.
От: BugZ Россия  
Дата: 09.04.07 11:07
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>//copy ctor и operator = совчем не обяз. определять. Дефолтные сойдут


Были определены исключительно для удобства отладки.

BZ>> my_int++ = ++my_int; //UB


Что означает UB?
Re[3]: Постфиксный инкремент для пользовательского типа.
От: Константин Л.  
Дата: 09.04.07 11:16
Оценка:
Здравствуйте, BugZ, Вы писали:

BZ>Здравствуйте, Константин Л., Вы писали:


КЛ>>//copy ctor и operator = совчем не обяз. определять. Дефолтные сойдут


BZ>Были определены исключительно для удобства отладки.


BZ>>> my_int++ = ++my_int; //UB


BZ>Что означает UB?


Undefined bеhavior — неопределнное поведение. В двух словах — результат работы твоей программы не определен.

Причем этот вопрос появляется с регулярностью в месяц.

Искать по ключевым словам: UB, точки следования, sequence points
Re[3]: Постфиксный инкремент для пользовательского типа.
От: Alco  
Дата: 09.04.07 11:18
Оценка:
Здравствуйте, BugZ, Вы писали:

BZ>Что означает UB?

undefined behavior (неопределенное поведение), и дальше обсуждать особо нечего.
Re[4]: Постфиксный инкремент для пользовательского типа.
От: BugZ Россия  
Дата: 09.04.07 11:39
Оценка:
Здравствуйте, Константин Л., Вы писали:

BZ>>>> my_int++ = ++my_int; //UB


С неопределенным поведением понятно, благодарю... А если как-нибудь упростить пример:


 MyInt my_int( 0 );
 MyInt my_int2( 1 );
 my_int++ = my_int2;



UB не будет? Меня больше интересует то, что находится слева.
Re[5]: Постфиксный инкремент для пользовательского типа.
От: Константин Л.  
Дата: 09.04.07 11:50
Оценка:
Здравствуйте, BugZ, Вы писали:

BZ>Здравствуйте, Константин Л., Вы писали:


BZ>>>>> my_int++ = ++my_int; //UB


BZ>С неопределенным поведением понятно, благодарю... А если как-нибудь упростить пример:



BZ>
BZ> MyInt my_int( 0 );
BZ> MyInt my_int2( 1 );
BZ> my_int++ = my_int2;
BZ>



BZ>UB не будет? Меня больше интересует то, что находится слева.


будет. Про const уже сказали.
Re: Постфиксный инкремент для пользовательского типа.
От: Roman Odaisky Украина  
Дата: 09.04.07 13:21
Оценка: 5 (2) +1
Здравствуйте, BugZ, Вы писали:

BZ>Объясните, пожалуйста, почему выполняется my_int++ = ++my_int; в приведённом ниже примере.


Я понимаю, затруднения вызывает вот такое?
template <class X>
X identity(X x)
{
    return x;
}

int main()
{
    std::string s1("hello");
    std::string s2("world");
    identity(s1) = s2; // no-op

    std::cout << s1 << " " << s2 << std::endl; // hello world
}

Результат identity(x) — non-const rvalue. Отсюда всё и следует. То, что выражение — rvalue, будет влиять только в том случае, если кто-то попытается напрямую взять его адрес. Хотя адрес у него есть и добыть его можно, см. ниже. В данном же выражении всего лишь вызывается (неконстантная) функция-член, а именно operator =.

Это на самом деле удобно, например:

std::string s = getSomePath() /* rvalue */ .replace(0, 4, "/tmp") /* то же самое rvalue -- избежали копирования */;


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



На грабли с rvalue наступают ленивые программеры, которые пишут так:
std::ofstream("/tmp/file") << "hello" << std::endl;

Казалось бы — замечательный способ открыть файл, записать туда строчку и сразу же его закрыть. Но на самом деле эта инструкция выводит совсем другое. Почему?
ostream& ostream::operator <<(void const *); // выводит адрес
ostream& operator <<(ostream &, char const *); // выводит строку

Который из них вызовется? Неконстантные ссылки к rvalue не привязываются, поэтому второй оператор выпадает из рассмотрения!

Чтобы вышеуказанный элегантный метод заработал, надо взять адрес rvalue-выражения std::ofstream("/tmp/file"). Легко:
ostream& ostream::flush();

Вот и адрес, voilà:
std::ofstream("/tmp/file").flush() << "hello" << std::endl;
До последнего не верил в пирамиду Лебедева.
Re[2]: Постфиксный инкремент для пользовательского типа.
От: Кодт Россия  
Дата: 09.04.07 13:47
Оценка: 3 (2) +1
Здравствуйте, Константин Л., Вы писали:

С какой это радости undefined? Максимум, unspecified. Тип-то пользовательский, и операторы перегружены.

Это такой тест на замыленность сознания экспертов Как увидел кучу инкрементов, так сразу UB.

Причина в другом.
Здесь выражение выглядит так
x++ = ++x;
// предстаёт как
x.post_increment().assignment( x.pre_increment() );

У rvalue x.post_increment() можно вызвать неконстантный метод assignment(). А вот превратить rvalue в неконстантное lvalue нельзя.
Т.е. если бы, например, было
MyInt& operator += (MyInt& lhs, MyInt const& rhs);
// = всегда трактуется как член (даже если определён неявно), и его нельзя сделать внешним.
// остальные - можно.

x++ += ++x;

то компилятор бы справедливо выругался.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[3]: Постфиксный инкремент для пользовательского типа.
От: Кодт Россия  
Дата: 09.04.07 13:49
Оценка:
К>У rvalue x.post_increment() можно вызвать неконстантный метод assignment(). А вот превратить rvalue в неконстантное lvalue нельзя.

Кстати, именно поэтому, если rvalue с самого начала const, то уже нельзя будет вызывать присваивание. Что мы и наблюдали у _nn_.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[3]: Постфиксный инкремент для пользовательского типа.
От: Константин Л.  
Дата: 09.04.07 14:03
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Константин Л., Вы писали:


К>С какой это радости undefined? Максимум, unspecified. Тип-то пользовательский, и операторы перегружены.


К>Это такой тест на замыленность сознания экспертов Как увидел кучу инкрементов, так сразу UB.


К>Причина в другом.

К>Здесь выражение выглядит так
К>
К>x++ = ++x;
К>// предстаёт как
К>x.post_increment().assignment( x.pre_increment() );
К>


ну чтож...согласен
Re[2]: Постфиксный инкремент для пользовательского типа.
От: BugZ Россия  
Дата: 09.04.07 14:07
Оценка:

Играй. И пой.
– Ща, ща... Как это... # Мама, мама, что я буду делать... #
– Класс!
– Чего?
– Класс, говорю.


Благодарю... Ваши с Кодтом объяснения помогли разобраться.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.