Я отлаживал программу в течение некоторого времени и в конце концов обнаружил, что ошибка произошла из-за того, что ссылка не обновлялась, как я думал, что это будет.
Вот пример, который показывает проблему, с которой я столкнулся:
#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?
Позвольте мне попытаться интерпретировать стандарт 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()
В C ++ нет гарантии того, в каком порядке оцениваются аргументы функции в одном выражении, даже если эти функции являются вызовами метода. Вы вызываете неопределенное поведение здесь, и это то, что вы получаете.
. оператор делает подразумевает последовательность, но только в том случае, если выражение перед. должен быть полностью оценен, прежде чем член получит доступ. Это не означает, что оценки подвыражений приостановлены до этого момента.
Кроме того, не проходите int
с const int&
нет, это не может быть быстрее, чем передать int
напрямую (если не по какой-то странной причине int
не вписывается в процессорное слово, и ссылка подходит).
Если добавить
Test& add(int& i, const int toadd)
{
i += toadd;
return *this;
}
Этот звонок
t.set(i).add(i, 5).print(i);
возвращается
15
Из этого я заключаю, что виновником является i + 5
в качестве параметра для печати.