Операнды сдвига упорядочены в C ++ 17

Я читал в C ++ 17 Standard $ 8.5.7.4:

Выражение E1 секвенировано перед выражением E2.

для операторов смены.

Также в правиле 19 cppreference говорится:

In a shift operator expression E1<<E2 and E1>>E2, every value
computation and side-effect of E1 is sequenced before every value
computation and side effect of E2

Но когда я пытаюсь скомпилировать следующий код с помощью gcc 7.3.0 или clang 6.0.0

#include <iostream>
using namespace std;

int main() {
    int i = 5;
    cout << (i++ << i) << endl;
    return 0;
}

Я получаю следующее предупреждение gcc:

../src/Cpp_shift.cpp: In function ‘int main()’:
../src/Cpp_shift.cpp:6:12: warning: operation on ‘i’ may be undefined [-Wsequence-point]
  cout << (i++ << i) << endl;
           ~^~

Предупреждение о лязгах:

warning: unsequenced modification and access to 'i' [-Wunsequenced]

Для компиляции я использовал следующие команды:

g++ -std=c++17 ../src/Cpp_shift.cpp -o Cpp_shift -Wall
clang++ -std=c++17 ../src/Cpp_shift.cpp -o Cpp_shift -Wall

Я получаю ожидаемый 320 как результат в обоих случаях (5 * 2 ^ 6)

Может кто-нибудь объяснить, почему я получаю это предупреждение? Я что-то упустил? Я также прочитал этот вопрос, связанный с этим не отвечаю на мой вопрос.

изменить: все другие варианты ++i << i, i << ++i и i << i++ приводят к тому же предупреждению.

edit2: (i << ++i) приводит к 320 для clang (правильно) и 384 для gcc (неверно). Кажется, что gcc дает неправильный результат, если ++ находится на E2, (i << i++) также дает неправильный результат.


person mch    schedule 10.08.2018    source источник
comment
Похоже, что диагностика создана логикой по старым правилам. Я не уверен, когда была введена последовательность <<, но, возможно, это было не раньше C ++ 14 или C ++ 17. Очевидно, что это ошибка, о которой в идеале следует сообщить.   -  person Cheers and hth. - Alf    schedule 10.08.2018
comment
@ Cheersandhth.-Alf Я посмотрел, предложение было добавлено стандартом C ++ 17.   -  person mch    schedule 10.08.2018
comment
Единственные ubsan-проверки, которые выполняет clang, - это добавление переполнения и смещение за пределы. Я не уверен, актуально это или нет. Удивительно, что оба компилятора выдают почти идентичные сообщения в одной и той же ситуации - обычно оба не ошибаются. Также интересно, что gcc сообщает, что может быть неопределенным.   -  person xaxxon    schedule 10.08.2018
comment
скажем, в CL будет 160 в случае 5. ++ выполняется после ‹----------------   -  person RbMm    schedule 10.08.2018
comment
@RbMm что такое cl?   -  person xaxxon    schedule 10.08.2018
comment
CL.EXE компилятор microsoft c ++. - просто проверьте это. godbolt.org/g/1tNmsK   -  person RbMm    schedule 10.08.2018
comment
с CL i++ << i выполняется как i << i; i++   -  person RbMm    schedule 10.08.2018
comment
на возврат (i ++ ‹< i); Я получаю разные результаты между clang и gcc - 320 против 160: godbolt.org/g/69La4v   -  person xaxxon    schedule 10.08.2018
comment
действительно думаю плохая вещь использовать такие уб (по смыслу) выражения.   -  person RbMm    schedule 10.08.2018
comment
скажем, сравните для gcc 4.7.4 (godbolt.org/g/UPsE2i) с gcc 4.8.1 ( godbolt.org/g/tJ6aGV) и выше   -  person RbMm    schedule 10.08.2018
comment
Когда я компилирую этот код с g++ -std=c++17 ../src/Cpp_shift.cpp -o Cpp_shift -Wall, я тоже получил это предупреждение, но когда я компилирую без флага -Wall, я вообще не получал никакого предупреждения. Это может быть предупреждение о кодировании этого флага. Итак, поскольку это не неопределенное поведение С ++ 17, проблема, вероятно, связана с реализацией этого флага.   -  person CoralK    schedule 10.08.2018
comment
@KorelK, когда я удаляю -Wall, я получаю тот же неправильный результат с gcc для (i << ++i). clang выдает предупреждение также без -Wall.   -  person mch    schedule 10.08.2018
comment
@mch Нет сомнений в том, что в gcc есть ошибка реализации, но предупреждение возникло из-за этого флага.   -  person CoralK    schedule 10.08.2018
comment
@RbMm, Версия MSVC на CE устарела, и в последнее время были внесены изменения соответствия, поэтому возможно, что последняя версия действительно соответствует.   -  person chris    schedule 10.08.2018
comment
@chris - тестирую на последних сборках cl   -  person RbMm    schedule 10.08.2018
comment
@xaxxon gcc дает 320 без -fsanitize=undefined и 160 с ним   -  person Tyker    schedule 10.08.2018
comment
Повторяющийся вопрос: stackoverflow.com/questions/51550156   -  person M.M    schedule 10.08.2018
comment
@ Тайкер такой странный. godbolt.org/g/7iCRdE   -  person xaxxon    schedule 10.08.2018
comment
Секвенирование C ++ 17 еще не совсем реализовано GCC. Проблема аналогичного характера в GCC с оператором присваивания: stackoverflow.com/questions/51511102/   -  person AnT    schedule 10.08.2018
comment
comment
@DidierL не совсем так, это еще и про неправильный результат, выданный gcc.   -  person mch    schedule 20.08.2018


Ответы (1)


В стандарте четко прописан порядок вычисления операндов оператора сдвига.

n4659 - §8.8 (p4):

Выражение E1 расположено перед выражением E2.

В выражении i++ << i нет неопределенного поведения, оно четко определено. Это ошибка в Clang и GCC оба.

person haccks    schedule 10.08.2018
comment
Чтобы быть точным: ошибка как при диагностике низкого качества, не обязательно ошибка как при несоответствии. Фактическое поведение во время выполнения могло быть / было исправлено для соответствия C ++ 17 до обновления диагностики. - person ; 10.08.2018
comment
@hvd кажется, что исправлено только поведение времени выполнения clang, gcc дает неправильные результаты, если приращение находится во втором выражении. - person mch; 10.08.2018
comment
@hvd; Теперь OP обновил вопрос, могу поспорить, что это ошибка в GCC. - person haccks; 10.08.2018
comment
Интересно. Сами GCC утверждают в поддержке стандартов C ++ в GCC, что новое поведение было реализовано в GCC 7. - person ; 10.08.2018
comment
Удивительно, что они оба были неправы в отношении такого фундаментально простого для тестирования изменения, как это - не только диагностики, но и фактических результатов. - person xaxxon; 10.08.2018
comment
@xaxxon; Иногда то, что кажется простым, становится трудным;) - person haccks; 10.08.2018
comment
Примеры в комментариях под основным вопросом показывают, что g ++ на самом деле неправильно реализует последовательность, это не просто фиктивное предупреждение. - person M.M; 10.08.2018
comment
Подобные проблемы возникают при разработке @xaxxon Clang. Сообщается, что Clang поддерживает переменную шаблона c ++ 14, в то время как создание экземпляра переменной-члена статического шаблона constexpr приводит к сбою clang с версии v3.9 и, как никогда, не исправлялось! Возможно, возрастающая сложность языка приводит к тому, что код компилятора становится все менее и менее исправляемым. - person Oliv; 10.08.2018
comment
@Oliv у вас есть ссылка на отчет об ошибке? - person xaxxon; 10.08.2018
comment
@xaxxon Я подозреваю законное подозрение: ошибка 38247. Если вам интересно, вы найдете много других похожих ошибок, связанных с шаблоном. - person Oliv; 10.08.2018
comment
@Oliv выглядит так, будто его рассматривали в 17-м году? godbolt.org/g/rJFzwu - person xaxxon; 10.08.2018
comment
@xaxxon Нет, он не был рассмотрен, как показано в ссылке на ваш Godbolt. Правильный вывод должен быть таким же, как gcc. Как бы то ни было, clang вылетает при третьем статическом утверждении. - person Oliv; 10.08.2018