Я думаю, что это ошибка в GCC.На основе [expr.ass] / 7 выражение
x |= y
эквивалентно
x = x | y
, за исключением того, что a
вычисляется только один раз.В побитовом ИЛИ, как и в других побитовых операциях, как также отмечалось в комментарии CoryKramer выше, обычные арифметические преобразования сначала будут выполняться для обоих операндов [expr.or] / 1 .Поскольку оба наших операнда имеют тип std::uint8_t
, обычные арифметические преобразования представляют собой просто целые преобразования [expr.arith.conv] /1.5.Интегральное повышение на std::uint8_t
должно означать, что оба операнда конвертируются в int
[conv.prom] / 1 .Никаких дальнейших преобразований операндов не требуется, поскольку преобразованные типы обоих операндов одинаковы.Наконец, int
, полученное из выражения |
, затем преобразуется обратно в std::uint8_t
и сохраняется в объекте, указанном в x
[expr.ass] / 3 .Я предположил бы, что этот последний шаг - то, что вызывает предупреждение в некоторых случаях.Однако нет никакого способа, чтобы результат побитового логического ИЛИ между двумя std::uint8_t
не мог быть представлен в std::uint8_t
, независимо от того, преобразуем ли мы в int
(который гарантированно будет больше *)1029 *) и обратно по пути.Таким образом, предупреждение здесь не нужно, и, вероятно, поэтому оно обычно не создается.
Единственное различие, которое я вижу между вашей первой и второй версией, заключается в том, что a()
- это значение, а d
- это значение.именующий.Однако категория значений не должна влиять на поведение обычных арифметических преобразований.Таким образом, предупреждение - ненужное или нет - должно, по крайней мере, производиться последовательноКак вы уже заметили, другие компиляторы, такие как clang, здесь не будут выдавать предупреждение.Кроме того, проблема, как представляется, странно специфична для участия вызовов функций в составном присваивании.Как отметил SergeyA в другом комментарии выше, GCC не будет выдавать предупреждение в эквивалентной форме c = c | a()
.Использование других типов значений вместо вызова функции, таких как, например, результат приведения литерала
c |= static_cast<std::uint8_t>(42);
, также не будет выдавать предупреждение в GCC .Но как только в правой части выражения появляется вызов функции, даже если результат вызова функции вообще не используется, например, в
c |= (a(), static_cast<std::uint8_t>(5));
предупреждениепоявляется .Таким образом, я бы пришел к выводу, что это ошибка в GCC, и было бы здорово, если бы вы написали отчет об ошибках ....