Программирование на C ++ было бы практически невозможным, если бы компилятору было разрешено создавать код, который соответствует описанному вами.
Это указано в §1.9 / 14 Выполнение программы (n3290):
Каждое вычисление значения и побочный эффект, связанный с полным выражением, секвенируются перед каждым вычислением значения и побочным эффектом, связанным со следующим полным выражением, которое будет оценено.
Ваш оператор return
упорядочен после предыдущего полного выражения.Компилятор должен вывести код , как если бы все побочные эффекты этого предыдущего оператора были полностью оценены, прежде чем он вычислит оператор return.
Ваш пример не соблюдает это правило, так как он оценивает *data
перед тем, как учесть побочные эффекты полного выражения std::call_once(...)
.
Кроме того, std::call_once
имеет это в своем описании (§30.4.4.2 / 2 и 3):
2 / Эффекты : Выполнение call_once, которое не вызывает его функцию, является пассивным выполнением.Выполнение call_once, которое вызывает его функцию, является активным выполнением.Активное выполнение должно вызвать INVOKE (DECAY_-
COPY ( std::forward<Callable>(func)), DECAY_COPY (std::forward<Args>(args))...)
.Если такой вызов func генерирует исключение, выполнение является исключительным, в противном случае оно возвращается.Исключительное выполнение должно распространить исключение вызывающей стороне call_once.Среди всех исполнений call_once для любого заданного Once_flag: самое большее одно должно быть возвращающим выполнением;если есть возвращающее выполнение, оно должно быть последним активным выполнением;и есть пассивные исполнения, только если есть возвратное исполнение.[ Примечание: пассивные исполнения позволяют другим потокам надежно наблюдать результаты, полученные при предыдущем возврате.- примечание конца ]
3 / Синхронизация : Для любого заданного флаг_выхла: все активные исполнения выполняются в общем порядке;завершение активного выполнения синхронизируется с началом следующего в этом общем порядке;и возвращающее выполнение синхронизируется с возвратом из всех пассивных выполнений.
Таким образом, стандарт требует синхронизации в соответствии с вашим вариантом использования.