Форум программистов, компьютерный форум CyberForum.ru

Почему операторы инкремента действуют по разному для стандартных и нестандартных типов? - C++

Восстановить пароль Регистрация
 
popelyuk
 Аватар для popelyuk
8 / 8 / 1
Регистрация: 04.12.2012
Сообщений: 130
19.01.2013, 23:17     Почему операторы инкремента действуют по разному для стандартных и нестандартных типов? #1
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class my {
    int i;
public:
    my(int in) :i(in) {}
    operator int () {
        return i;
    }
    int operator=(int in) {
        i=in; return i;
    }
    int operator ++() {
        i=i+7;
        return i;
    }
    int operator ++(int) {
        int tmp=i;
        i=i+7;
        return tmp;
    }
    int operator --() {
        i=i-7;
        return i;
    }
    int operator --(int) {
        int tmp=i;
        i=i-7;
        return tmp;
    }
};
 
 
int main() {
    my m=100; int s=100;
    int res_s=s++ + s++;    // почему здесь res = 200
    int res_m=m++ + m++;  // а зесь нет??
     
    std::cout<<"res_s="<<res_s<<std::endl;
    std::cout<<"res_m="<<res_m<<std::endl;
    return 0;
}
Вот что получаем на выходе:
Почему операторы инкремента действуют по разному для стандартных и нестандартных типов?
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
19.01.2013, 23:17     Почему операторы инкремента действуют по разному для стандартных и нестандартных типов?
Посмотрите здесь:

Почему функция strchr по разному работает при разных строках? C++
операторы преобразования типов C++
C++ Найти предельные значения для целочисленных типов. Не использовать заранее определенные константы границ типов.
C++ Операторы преобразования типов
C++ Почему одинаковые коды работают по разному?
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
lemegeton
 Аватар для lemegeton
2908 / 1337 / 133
Регистрация: 29.11.2010
Сообщений: 2,720
19.01.2013, 23:27     Почему операторы инкремента действуют по разному для стандартных и нестандартных типов? #2
Это undefined behaviour, поскольку последовательность выполнения операций (не приоритет, а именно последовательность) не определена.
Не буду вдаваться в детали, но компилятор определяет в какой момент выполнения производится инкремент/декремент операнда, стандартом это не определено. Поэтому результат и странен.
OhMyGodSoLong
~ Эврика! ~
 Аватар для OhMyGodSoLong
1234 / 983 / 42
Регистрация: 24.07.2012
Сообщений: 2,002
19.01.2013, 23:33     Почему операторы инкремента действуют по разному для стандартных и нестандартных типов? #3
Потому что для пользовательских типов перегруженные операторы — это вызовы функций, а для встроенных их родные операторы — нет.

Есть такая вещь — sequence point (точка следования). Это точка в коде, до которой должны выполниться все побочные эффекты (все присваивания, которые по каким-то причинам отложили, например; в частности, присваивания, неявно возникающие при инкрементах). Точками следования, в частности, являются вызовы функций (для внутренних побочных эффектов и вычислений в аргументах), а также полные операторы (грубо, точки с запятой).

Так вот, в вашей строке 34 всего одна точка следования — аккурат на точке с запятой. Компилятор может извращаться со значением переменной как хочет, но после точки с запятой она должна быть увеличена на два. Не факт, правда, что увеличение будет происходить в два этапа. Ваш компилятор, например, сначала подставил старые значения, а потом вписал увеличение на два. А чей-то другой может вычислять по шагам: запомнить старое, увеличить на один, прибавить увеличенное на один к старому, увеличить ещё раз на единицу. В результате выведет 201 и будет прав. Это, что называется, неопределённое поведение.

А вот в строке 35 точек следования три: по одной на каждом инкременте, так как m++ + m++ это на самом деле operator++(m) + operator++(m), ну и одна в конце выражения. Поэтому здесь компилятор просто обязан выполнять инкременты последовательно: сначала выдать старое значение, потом увеличить его на 7, потом прибавить к старому значению увеличенное на 7 и снова увеличить на 7, выдавая в итоге 207.
popelyuk
 Аватар для popelyuk
8 / 8 / 1
Регистрация: 04.12.2012
Сообщений: 130
19.01.2013, 23:33  [ТС]     Почему операторы инкремента действуют по разному для стандартных и нестандартных типов? #4
Цитата Сообщение от lemegeton Посмотреть сообщение
Это undefined behaviour, поскольку последовательность выполнения операций (не приоритет, а именно последовательность) не определена.
Не буду вдаваться в детали, но компилятор определяет в какой момент выполнения производится инкремент/декремент операнда, стандартом это не определено. Поэтому результат и странен.
а как же вот эта табличка, тут оператор инкремента/дикримента в самом конце после равно и прочих:
Почему операторы инкремента действуют по разному для стандартных и нестандартных типов?
popelyuk
 Аватар для popelyuk
8 / 8 / 1
Регистрация: 04.12.2012
Сообщений: 130
19.01.2013, 23:36  [ТС]     Почему операторы инкремента действуют по разному для стандартных и нестандартных типов? #5
Цитата Сообщение от ~OhMyGodSoLong~ Посмотреть сообщение
Потому что для пользовательских типов перегруженные операторы — это вызовы функций, а для встроенных их родные операторы — нет.

Есть такая вещь — sequence point (точка следования). Это точка в коде, до которой должны выполниться все побочные эффекты (все присваивания, которые по каким-то причинам отложили, например; в частности, присваивания, неявно возникающие при инкрементах). Точками следования, в частности, являются вызовы функций (для внутренних побочных эффектов и вычислений в аргументах), а также полные операторы (грубо, точки с запятой).

Так вот, в вашей строке 34 всего одна точка следования — аккурат на точке с запятой. Компилятор может извращаться со значением переменной как хочет, но после точки с запятой она должна быть увеличена на два. Не факт, правда, что увеличение будет происходить в два этапа. Ваш компилятор, например, сначала подставил старые значения, а потом вписал увеличение на два. А чей-то другой может вычислять по шагам: запомнить старое, увеличить на один, прибавить увеличенное на один к старому, увеличить ещё раз на единицу. В результате выведет 201 и будет прав. Это, что называется, неопределённое поведение.

А вот в строке 35 точек следования три: по одной на каждом инкременте, так как m++ + m++ это на самом деле operator++(m) + operator++(m), ну и одна в конце выражения. Поэтому здесь компилятор просто обязан выполнять инкременты последовательно: сначала выдать старое значение, потом увеличить его на 7, потом прибавить к старому значению увеличенное на 7 и снова увеличить на 7, выдавая в итоге 207.
Спасибо, очень хороший ответ
Yandex
Объявления
19.01.2013, 23:36     Почему операторы инкремента действуют по разному для стандартных и нестандартных типов?
Ответ Создать тему
Опции темы

Текущее время: 16:48. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru