Что происходит до того, как это означает в спецификации С++ 11?

Я пытаюсь понять значение происходит раньше в спецификации C++11 и, в частности, предполагает ли спецификация какое-либо неформальное понимание термина в дополнение к тому, что указано. Я работаю с проекта N3290.

Прямым аргументом в пользу того, что термин следует интерпретировать только в отношении самой спецификации, является то, что спецификация фактически говорит о собственном определении термина. Например, 1.10.6: «происходит до (как определено ниже)» или 1.10.11: «происходит до» отношения, определенного ниже.

С другой стороны, 1.10.12 гласит: «Оценка A происходит раньше, чем оценка B, если: …» Если бы это было определение «происходит раньше», я бы ожидал, что оно будет читаться как «если и только если." Таким образом, похоже, что спецификация определяет некоторые минимальные требования к отношению «происходит до того, как это произойдет», но оставляет его открытым для других факторов, которые могут включать некоторое неформальное представление о том, что означает этот термин, или могут включать формулировку в других частях спецификации.

Затем параграф 1.10.13 определяет видимый побочный эффект или нет? Это похоже на определение, потому что видимый побочный эффект выделен курсивом, но определение дано в терминах «происходит раньше», так что следует ли это в качестве альтернативы читать как дополнительное ограничение на «происходит до», учитывая интуитивное понятие видимого побочного эффекта? Другими словами, если B видит A, означает ли это, что A произошло до B (дальнейшее ограничение на определение «происходит до»), или это импликация предназначена только в другом направлении, а именно, что когда A происходит до B, то A должен быть видимый побочный эффект.

Вот конкретный пример (на основе этот вопрос, но здесь я просто пытаюсь понять, что происходит раньше, и в отношении C++, а не C, поскольку спецификация C++ кажется немного более ясной):

#include <atomic>
#include <cassert>
#include <thread>

using namespace std;

atomic<int> a;
atomic<int> b;

void
p1()
{
  atomic_store_explicit(&a, 1, memory_order_relaxed);       // 1
}

void
p2()
{
  if (atomic_load_explicit(&a, memory_order_relaxed)) {
    //atomic_thread_fence(memory_order_release);
    atomic_store_explicit(&b, 1, memory_order_relaxed);     // 2
  }
}

void
p3()
{
  int local = 1;
  if (atomic_load_explicit(&b, memory_order_relaxed)) {
    //atomic_thread_fence(memory_order_acquire);
    local = atomic_load_explicit(&a, memory_order_relaxed); // 3
  }
  assert (local == 1);
}

int
main()
{
  thread t1{p1}, t2{p2}, t3{p3};
  t1.join();
  t2.join();
  t3.join();
}

Учитывая выполнение, в котором выполняется строка, отмеченная // 3, требует ли спецификация, чтобы строка, отмеченная // 1, выполнялась перед любой другой строкой в ​​программе? Неформально строка // 1 является видимым побочным эффектом в строке // 2, значит ли это, что // 1 произошло до // 2, и в этом случае эквивалентный аргумент говорит, что // 2 произошло до // 3, и утверждение не может быть ошибочным? По чисто внешним причинам (а именно, понимание того, как работает NUMA, и предположение, что комитет по языку хотел оставить место для упрощенной оптимизации порядка памяти), я думаю, что это происходит до того, как условия, вероятно, не выполняются, и утверждение может потерпеть неудачу.

Мой следующий вопрос: влияет ли раскомментирование заборов в p2 и p3 на то, происходит ли // 1 перед любым другим утверждением? Здесь я подозреваю, что если до соединения между // 1 и кодом в других потоках уже не произошло что-то, раскомментирование заборов в других потоках не изменит этого. (Хотя это означало бы, что нет способа предотвратить ошибку утверждения, используя только заборы.)

В качестве последнего замечания, у меня есть эта проблема, когда спецификация содержит абстрактный язык об A, B, M, X, Y, который, кажется, подразумевает одну вещь, пока я на самом деле не пойду и не попытаюсь сопоставить A, B и т. д. с операторами и переменными. в моем коде, и в этот момент это, кажется, подразумевает что-то еще. Поэтому я надеюсь получить ответ, который действительно связывает спецификацию C++ с кодом примера, что может показаться чрезмерно педантичным. (Например, простое утверждение, что // 1 происходит перед // 3 из-за когерентности чтения-чтения, не отвечает на мой вопрос — мой вопрос больше о том, как конкретное определение когерентности чтения-чтения в спецификации С++ 11 применяется к моему конкретному примеру кода. , и о том, на каком основании предполагается, что определение когерентности чтения-чтения управляет определением «происходит раньше», а не наоборот.)


person user3188445    schedule 12.12.2015    source источник
comment
Вы слишком много думаете об этом. 1.10/12 — это тоже определение, о чем свидетельствует тот факт, что происходит до выделено здесь курсивом. 1.3/3 Термины, которые используются только в небольшой части настоящего Международного стандарта, имеют определения там, где они используются, и выделены курсивом там, где они определены.   -  person Igor Tandetnik    schedule 12.12.2015
comment
Я подозреваю, что это просто личный стиль того, кто написал формулировку для 1.10: есть ряд определений, введенных с помощью if, а не if and only if. См. 1.10/4 конфликт, 1.10/9 несет зависимость, 1.10/10 в зависимости от порядка и так далее.   -  person Igor Tandetnik    schedule 13.12.2015
comment
@IgorTandetnik: Возможность читать, если как если бы и только если, очень полезна и делает все понятным. Я буду рад выбрать ваш ответ, если вы поместите свои два комментария в ответ. Если нет, то отвечу сам на основании ваших аргументов.   -  person user3188445    schedule 13.12.2015