Ошибка GCC? Методы сцепления, точка прерывистой последовательности

Я отлаживал программу в течение некоторого времени и в конце концов обнаружил, что ошибка произошла из-за того, что ссылка не обновлялась, как я думал, что это будет.

Вот пример, который показывает проблему, с которой я столкнулся:

#include <iostream>
using namespace std;

struct Test {
Test& set(int& i){ i = 10; return *this; }
Test& print(const int& i){ cout << i << endl; return *this; }
};

int main(void){
int i = 0;
Test t;

t.set(i).print(i + 5);

return 0;
}

Я ожидал, что метод print () выведет 15, но вместо этого выдает 5.

РЕДАКТИРОВАТЬ: 10 дней спустя я только что понял, что с Clang это выдает 15! Это ошибка в GCC?

15

Решение

Позвольте мне попытаться интерпретировать стандарт C ++ 11 на этом. В §1.9 / 15 говорится:

За исключением отмеченных случаев, оценки операндов отдельных операторов и подвыражений отдельных выражений не являются последовательными. […] Если побочный эффект на скалярный объект не секвенирован относительно другого побочного эффекта на тот же скалярный объект или вычисления значения с использованием значения того же скалярного объекта, поведение не определено.

Конечно int является скалярным типом, и t.set(i).print(i + 5); содержит побочный эффект на i в set() и вычисление значения i + 5поэтому, если не указано иное, поведение действительно не определено. Читая §5.2.5 («Доступ к членам класса»), я не смог найти никаких заметок о последовательностях, касающихся . оператор. [Но см. Правку ниже!]

Обратите внимание, однако, что, конечно, гарантируется, что set() выполняется раньше print() потому что последний получает возвращаемое значение первого как (неявный this) аргумент.
Виновником здесь является то, что вычисление значения для printаргументы unsequenced неопределенно последовательность относительно вызова set,

РЕДАКТИРОВАТЬ: После прочтения ответа в вашем (@ Xeno’s) комментарии, я перечитал параграф в стандарте, и на самом деле он позже говорит:

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

Так как неопределенно последовательность не является unsequenced («выполнение непоследовательных оценок может перекрываться», §1.9 / 13), это действительно не неопределенное поведение, а «просто» неопределенное поведение, означающее, что и 15, и 5 являются правильными выходными данными.

Так когда < означает «последовательность перед» и ~ означает «неопределенно упорядоченный», мы имеем:

(value computations for print()'s arguments ~ execution of set()) < execution of print()

10

Другие решения

В C ++ нет гарантии того, в каком порядке оцениваются аргументы функции в одном выражении, даже если эти функции являются вызовами метода. Вы вызываете неопределенное поведение здесь, и это то, что вы получаете.

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

Кроме того, не проходите intс const int&нет, это не может быть быстрее, чем передать int напрямую (если не по какой-то странной причине int не вписывается в процессорное слово, и ссылка подходит).

2

[слишком долго для комментария:]

Если добавить

Test& add(int& i, const int toadd)
{
i += toadd;
return *this;
}

Этот звонок

t.set(i).add(i, 5).print(i);

возвращается

15

Из этого я заключаю, что виновником является i + 5 в качестве параметра для печати.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector