Given the code from here:
class lazy_init
{
mutable std::once_flag flag;
mutable std::unique_ptr<expensive_data> data;
void do_init() const
{
data.reset(new expensive_data);
}
public:
expensive_data const& get_data() const
{
std::call_once(flag,&lazy_init::do_init,this);
return *data;
}
};
And I saw a few variants of the same pattern elsewhere also. So my question is: why this code is considered save? and why compiler can’t just read data before calling std::call_once and ends up with an incorrect data?
e.g
tmp = data.get();
std::call_once(flag,&lazy_init::do_init,this);
return *tmp;
I mean I have found nothing about any barriers which would prevent that.
Programming in C++ would be essentially impossible if the compiler was allowed to produce code that matched what you describe.
This is stated in §1.9/14 Program Execution (n3290):
Your
returnstatement is sequenced after the preceding full expression. The compiler has to output code as if all side-effects of that preceding statement has been completely evaluated before it evaluates the return statement.Your example doesn’t respect that rule, since it evaluates
*databefore taking into account the side-effects of thestd::call_once(...)full expression.Additionally,
std::call_oncehas this in its description (§30.4.4.2/2 and 3):So the standard mandates synchronization to fit your use-case.