Я считаю, что cppreference - это правильно. Я думаю, что это сводится к правилу «как будто» [intro.execution] / 1 . Компиляторы обязаны воспроизводить только наблюдаемое поведение программы, описанное вашим кодом. Отношение sequenced-before устанавливается только между оценками с точки зрения потока, в котором эти оценки выполняются [intro.execution] / 15 . Это означает, что когда две оценки, последовательно расположенные одна за другой, появляются где-то в каком-то потоке, код, фактически выполняющийся в этом потоке, должен вести себя , как если бы независимо от того, что первая оценка действительно влияла на то, что делает вторая оценка. Например,
int x = 0;
x = 42;
std::cout << x;
должен print 42. Однако компилятору на самом деле не нужно сохранять значение 42 в объекте x
перед чтением значения из этого объекта в распечатай это. Он также может помнить, что последним значением, которое будет сохранено в x
, было 42, а затем просто напечатать значение 42 непосредственно перед выполнением фактического сохранения значения 42 в x
. Фактически, если x
является локальной переменной, она может также просто отслеживать, какое значение этой переменной было назначено последним в любой точке, и никогда даже не создавать объект или фактически хранить значение 42. Поток не может сказать разница. Поведение всегда будет , как если бы была переменная, а , как если бы , значение 42 было фактически сохранено в объекте x
до загрузки из этот объект. Но это не значит, что сгенерированный машинный код должен хранить и загружать что-либо где-либо. Все, что требуется, - это то, что наблюдаемое поведение сгенерированного машинного кода неотличимо от того, что было бы, если бы все эти вещи действительно происходили.
Если мы посмотрим на
r2 = x.load(std::memory_order_relaxed); // C
y.store(42, std::memory_order_relaxed); // D
тогда да, C упорядочено перед D. Но если смотреть из этого потока изолированно, ничто из того, что C не влияет на результат D. И ничто из того, что делает D, не изменило бы результат C. Единственный способ, которым один может повлиять на другого, - это косвенное последствие того, что что-то происходит в другом потоке. Однако, указав std::memory_order_relaxed
, вы явно заявили , что порядок, в котором загрузка и сохранение выполняются другим потоком, не имеет значения. Поскольку никакой другой поток не может наблюдать за загрузкой и хранением в каком-либо конкретном порядке, другой поток не может ничего сделать, чтобы C и D согласованно влияли друг на друга. Таким образом, порядок, в котором загрузка и хранение фактически выполняются, не имеет значения. Таким образом, компилятор может изменить их порядок. И, как упоминалось в пояснении под этим примером, если сохранение из D выполняется до загрузки из C, то r1 == r2 == 42 действительно может произойти ...