Поведение оператора ==
От: seejay Россия  
Дата: 07.11.13 15:31
Оценка: -1 :)
Не могу понять, почему для int* порядок вычисления аргументов оператора == один, а для структуры другой.
#include <iostream>

struct A{
    int *m;
    int c;

    A(int* p) :m(p), c(0){ }

    A& operator++(){ ++c; return *this; }
    int* operator&(){ return &m[c]; }
};

int main() {
    int m[10] = {0,1,2,3};

    int *pi = m;
    if (&pi == &++pi)
        std::cout << "pint ok";

    A p(m);
    if (&p == &++p)
        std::cout << "pA ok";

    return 0;
}


Вывод программы:
pint ok


То есть в первом if-e порядок вычисления такой:

    &++pi
    &pi
    operator==

Во втором, для A такой:

    &p
    &++p
    operator==
operator==
Re: Поведение оператора ==
От: watchmaker  
Дата: 07.11.13 17:52
Оценка: 2 (2) +2
Здравствуйте, seejay, Вы писали:

S>Не могу понять, почему для int* порядок вычисления аргументов оператора == один, а для структуры другой.

Компилятор имеет право выбирать любой порядок вычислений операндов оператора==.



S>    if (&pi == &++pi)

Тут ты сравниваешь адрес переменной pi с адресом переменной pi. Невероятно, но адрес переменной не зависит от её значения. Такое условие будет истинно всегда, независимо от выбранного порядка.
Re: Поведение оператора ==
От: _niko_ Россия  
Дата: 07.11.13 19:18
Оценка:
Здравствуйте, seejay, Вы писали:

S>Не могу понять, почему для int* порядок вычисления аргументов оператора == один, а для структуры другой.



Не могу сослаться на конкретный пункт стандарта, но то в какой последовательности компилятору вычислять левую и правую часть сравнения стандарт не оговаривает,
тут действуют те же правила что и при передачи параметров в функцию (по факту тут и есть что то типа вызова оператора сравнения).

Например ниже приведенный пример демонстрирует следующее:
— сперва применяется инкримент для всех переменных условия
— потом там всякие арифметические операции...
— потом берутся значения по адресам опять же для всех переменных условия
— и потом применяется посткримент, опять для всех переменных условия

#include <iostream>

int main()
{
    int m[10] = {0,1,2,3};

    {
        int * pi_0 = m;
        std::cout << ((*  pi_0 == *++pi_0) ? "ok\n" : "not ok\n");  // № 1
    }
    {
        int * pi_0 = m;
        std::cout << ((*++pi_0 == *  pi_0) ? "ok\n" : "not ok\n");  // № 2
    }
    {
        int * pi_0 = m;
        std::cout << ((*  pi_0 == *pi_0++) ? "ok\n" : "not ok\n");  // № 3
    }
    {
        int * pi_0 = m;
        std::cout << ((*pi_0++ == *  pi_0) ? "ok\n" : "not ok\n");  // № 4
    }
    {
        int * pi_0 = m;
        std::cout << ((*  pi_0 == *(pi_0+sizeof(*++pi_0))) ? "ok\n" : "not ok\n");  // № 5
    }
    {
        int * pi_0 = m;
        std::cout << ((*(pi_0+sizeof(*++pi_0)) == *  pi_0) ? "ok\n" : "not ok\n");  // № 6
    }
    {
        int * pi_1 = m;
        int * pi_2 = m;
        ++pi_2;
        std::cout << ((*  pi_1 == *  pi_2) ? "ok\n" : "not ok\n");  // № 7
    }

    return 0;
}


output:
ok
ok
ok
ok
ok
ok
not ok
Re[2]: Поведение оператора ==
От: _niko_ Россия  
Дата: 07.11.13 19:30
Оценка:
Здравствуйте, _niko_, Вы писали:

__>Здравствуйте, seejay, Вы писали:


S>>Не могу понять, почему для int* порядок вычисления аргументов оператора == один, а для структуры другой.


__>...


С примерами №5 и №6 осечка вышла
#include <iostream>

int main()
{
    int m[10] = {0,1,2,3,4,5,6,7,8,9};

    {
        int * pi_0 = m;
        std::cout << ((*  pi_0 == *++pi_0) ? "ok\n" : "not ok\n");  // № 1
    }
    {
        int * pi_0 = m;
        std::cout << ((*++pi_0 == *  pi_0) ? "ok\n" : "not ok\n");  // № 2
    }
    {
        int * pi_0 = m;
        std::cout << ((*  pi_0 == *pi_0++) ? "ok\n" : "not ok\n");  // № 3
    }
    {
        int * pi_0 = m;
        std::cout << ((*pi_0++ == *  pi_0) ? "ok\n" : "not ok\n");  // № 4
    }
    {
        int * pi_0 = m;
        std::cout << ((*  pi_0 == *(++pi_0 + 1)) ? "ok\n" : "not ok\n");  // № 5
    }
    {
        int * pi_0 = m;
        std::cout << ((*(++pi_0 + 1) == *  pi_0) ? "ok\n" : "not ok\n");  // № 6
    }
    {
        int * pi_1 = m;
        int * pi_2 = m;
        ++pi_2;
        std::cout << ((*  pi_1 == *  pi_2) ? "ok\n" : "not ok\n");  // № 7
    }

    return 0;
}

output (компилятор от MSVC 2012):
ok
ok
ok
ok
not ok
not ok
not ok
Re: Поведение оператора ==
От: Кодт Россия  
Дата: 08.11.13 07:27
Оценка:
Здравствуйте, seejay, Вы писали:

S>Не могу понять, почему для int* порядок вычисления аргументов оператора == один, а для структуры другой.


Потому что для int* у тебя неопределённое поведение, а для структуры — неспецифицированное.

В случае структуры получается, условно говоря, f(g()) == f()
Какие функции в каком порядке вызовутся — это личное дело компилятора (хотя в дебаге, вероятнее всего, сперва будет правая f(), затем левая g(), затем левая f()).
А поскольку они ещё и с побочными эффектами и влияют друг на друга, то может получиться что угодно.

В случае голого указателя вообще говорить не о чем, это классика UB: чтение и изменение вперемешку. Ничем не хуже классики i + i++.
Перекуём баги на фичи!
Re[2]: Поведение оператора ==
От: rg45 СССР  
Дата: 08.11.13 08:17
Оценка:
Здравствуйте, Кодт, Вы писали:


    int m[10] = {0,1,2,3};

    int *pi = m;
    if (&pi == &++pi)
        std::cout << "pint ok";


К>В случае голого указателя вообще говорить не о чем, это классика UB: чтение и изменение вперемешку. Ничем не хуже классики i + i++.


Да нет же. Изменяется значение переменной, а читается ее адрес.

Кроме того, на сколько я помню, "классикой" (до отмены точек следования) было что-то вроде "i = i++" — т.е. повторное изменение переменной между соседними точками следования.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[3]: Поведение оператора ==
От: Кодт Россия  
Дата: 08.11.13 09:14
Оценка:
Здравствуйте, rg45, Вы писали:

К>>В случае голого указателя вообще говорить не о чем, это классика UB: чтение и изменение вперемешку. Ничем не хуже классики i + i++.

R>Да нет же. Изменяется значение переменной, а читается ее адрес.

А, точно! Прощёлкал глазами.

R>Кроме того, на сколько я помню, "классикой" (до отмены точек следования) было что-то вроде "i = i++" — т.е. повторное изменение переменной между соседними точками следования.

Ну, если уж о классике говорить, то i++ + ++i

А как нынешний стандарт разруливает такие конструкции? Ну, кроме того, что точки следования по-другому называются.
Один же леший, неопределённое поведение.

Причём, это ведь не издевательство компиляторостроителей над новичками, а суровая правда жизни:
int foo(int& i, int& j) { return i++ + ++j; } // можно оптимизировать как угодно (казалось бы...)
int bar(int& i) { foo(i, i); }
Перекуём баги на фичи!
Re[2]: Поведение оператора ==
От: seejay Россия  
Дата: 08.11.13 11:22
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Тут ты сравниваешь адрес переменной pi с адресом переменной pi. Невероятно, но адрес переменной не зависит от её значения. Такое условие будет истинно всегда, независимо от выбранного порядка.


А ведь и в самом деле. Туплю.
Re[4]: Поведение оператора ==
От: rg45 СССР  
Дата: 08.11.13 14:12
Оценка:
Здравствуйте, Кодт, Вы писали:

К>А как нынешний стандарт разруливает такие конструкции? Ну, кроме того, что точки следования по-другому называются.


Это я затрудняюсь ответить. Там же все упростили, поэтому сходу и не разберешься
--
Не можешь достичь желаемого — пожелай достигнутого.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.