Последовательность в составном условном выражении

Оператор if в следующем примере взят из старого проекта, который я пытаюсь построить снова. Мне жаль, что это не проверяемый образец в том смысле, что он не воспроизводит ошибку, он сам по себе отлично компилируется.

enum S { };

struct R {
    S type, state;
    double height;
};

int main ()
{
    int rows;
    S alive, rc;
    double h;
    R *r, *n;

    // BEGIN
    if ( (((r = n-1)   ->type & rc) && h > r->height) ||
         (((r = n+1)   ->type & rc) && h > r->height) ||
         (((r = n-rows)->type & rc) && h > r->height) ||
         (((r = n+rows)->type & rc) && h > r->height) )
    {
        n->state = alive;
    }
    // END
}

Однако внутри проекта при той же компиляции (clang 3.3 без опций, кроме путей включения) это дает

warning: multiple unsequenced modifications to 'r' [-Wunsequenced]

(указывая на подвыражение r = n - 1). Оператор if (отмечен BEGIN-END) внутри проекта идентичен, только типы переменных могут отличаться. Типы, определенные здесь, являются лишь грубыми приближениями, но я думаю, что они не имеют отношения к предупреждению. Я смог упростить выражение до

if( (r = n) || (r = n) ) //...

при этом предупреждение воспроизводится в проекте.

Насколько я понимаю, после оценки первого операнда операторов &&, || (когда они не перегружены) есть точка последовательности. Поэтому я не могу объяснить предупреждение.

Если есть какие-либо идеи относительно формы выражения независимо от типов, это поможет.

ИЗМЕНИТЬ

Используя комментарии Вона Катона, я также обнаружил, что

if( ((r = n-1) ->type) || ((r = n+1) ->type) )

выдает предупреждение, а

if( bool((r = n-1) ->type) || bool((r = n+1) ->type) )

не. S на самом деле является классом, который имитирует перечисление и имеет собственный базовый класс. Он имеет перегруженные побитовые операторы &, | и ! для маскирования, а также оператор неявного преобразования в базовый тип, которым в конкретном случае является size_t. Я не знаю, насколько это поможет, но я сожалею, что не раскрыл более полную информацию с самого начала.


person iavr    schedule 02.04.2014    source источник
comment
Могут ли задействованные типы иметь определяемые пользователем операторы && и ||? Только встроенные операторы имеют гарантию последовательности.   -  person Vaughn Cato    schedule 02.04.2014
comment
Перегружен только operator&, но предупреждение остается даже в упрощенном выражении без &. Кроме того, r,n — это указатели, поэтому я не думаю, что смогу перегрузить что-то еще.   -  person iavr    schedule 02.04.2014
comment
+1: Это глубокий вопрос. Я бросил ответ в кольцо, но с интересом жду более подробного ответа.   -  person Bathsheba    schedule 02.04.2014
comment
Является ли тип r в проекте также необработанным указателем?   -  person Vaughn Cato    schedule 02.04.2014
comment
Обратите внимание, что в настоящее время концепция точек последовательности устарела в пользу четко определенной стандартной многопоточной модели памяти.   -  person Manu343726    schedule 02.04.2014
comment
@VaughnCato Да, r — это необработанный указатель. R — это обычный struct. S на самом деле не enum, а скорее класс-оболочка enumeration, который тогда был попыткой имитировать наличие моего собственного базового типа.   -  person iavr    schedule 02.04.2014
comment
@ Manu343726 Manu343726 Теперь, когда вы упомянули модель памяти, R фактически содержала union (для информации, которая использовалась во взаимоисключающих моментах времени), но type,state,height занимала разные позиции. Только height делил ту же позицию с другим участником в union.   -  person iavr    schedule 02.04.2014
comment
В своем проекте попробуйте использовать if( bool(r = n) || bool(r = n) ) и посмотрите, получите ли вы такое же предупреждение.   -  person Vaughn Cato    schedule 02.04.2014
comment
@VaughnCato Интересная идея... и предупреждение ушло :-)   -  person iavr    schedule 02.04.2014
comment
Я думаю, что в вашей среде должен быть перегруженный operator ||, который каким-то образом вызывается, и приведение к bool позволяет избежать использования перегрузки.   -  person Vaughn Cato    schedule 02.04.2014
comment
@VaughnCato Пожалуйста, посмотрите мое редактирование, это может помочь.   -  person iavr    schedule 02.04.2014
comment
Вы когда-нибудь догадывались об этом?   -  person Shafik Yaghmour    schedule 10.04.2014
comment
@ShafikYaghmour Нет, потому что я еще не изучал это. Я подозреваю перегруженные операторы | и &, но не уверен. Когда я попытаюсь снова, мне может сойти с рук трюк с bool(), замена S на настоящий enum с пользовательским базовым типом или что-то еще, что может появиться. Я рад, что уже есть обходной путь, потому что это действительно умное выражение, которое я не хотел бы менять.   -  person iavr    schedule 10.04.2014


Ответы (2)


См. N3797 1.9/15

Если не указано иное, вычисления операндов отдельных операторов и подвыражений отдельных выражений не упорядочены.

5.14/2: (оператор &&)
5.15/2: (оператор ||)

Если вычисляется второе выражение, каждое вычисление значения и побочный эффект, связанные с первым выражением, упорядочиваются перед каждым вычислением значения и побочным эффектом, связанным со вторым выражением.

Ссылка на «побочный эффект» означает, что значение r имеет правильную последовательность.

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

person david.pfx    schedule 02.04.2014

Здесь обычная путаница. Тот факт, что || и && являются точками последовательности, не означает, что среда выполнения оценивает r в том порядке, в котором вы думаете.

Он может вычислять r = n - 1, r = n + 1 и т. д. прежде, чем любое из if выражений будет вычислено в любом порядке; т. е. не имеет последовательности.

Это то, что предупреждение компилятора подчеркивает вам.

Между прочим, даже языки, которые претендуют на более четкое определение последовательности (например, Java), будут страдать от эффекта, на который я здесь обращаю ваше внимание!

person Bathsheba    schedule 02.04.2014
comment
Извините, я не совсем понимаю. Помимо точек последовательности, требуется замыкание и порядок оценки, верно? - person iavr; 02.04.2014
comment
Да, действительно есть, но среда выполнения все еще может попытаться предварительно вычислить все r = выражения присваивания. На этом сайте есть несколько настоящих экспертов, которые смогут подробно ответить на этот вопрос: давайте посмотрим, что у нас получится. - person Bathsheba; 02.04.2014
comment
Вы говорите, что среда выполнения может вычислять количества в другом порядке, чем указано в языке? Это так сложно уследить... - person iavr; 02.04.2014
comment
Ваши назначения для r могут быть рассчитаны еще до начала оценки if. И они непоследовательны, как я говорю в своем ответе. - person Bathsheba; 02.04.2014