In the following code a member function set()
is called on a model
, which is a null pointer. This would be undefined behavior. However, the parameter of the member function is a result of another function call that checks whether the model
is a null pointer and throws in that case. Is it guaranteed that estimate()
will always be called before the model
is accessed or is it still Undefined Behaviour (UB)?
#include <iostream>
#include <memory>
#include <vector>
struct Model
{
void set(int x)
{
v.resize(x);
}
std::vector<double> v;
};
int estimate(std::shared_ptr<Model> m)
{
return m ? 3 : throw std::runtime_error("Model is not set");
}
int main()
{
try
{
std::shared_ptr<Model> model; // null pointer here
model->set(estimate(model));
}
catch (const std::runtime_error& e)
{
std::cout << e.what();
}
return 0;
}
This is still undefined behavior (UB) as per expr.compound:
The postfix-expression is sequenced before each expression in the expression-list and any default argument. The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.
(emphasis mine)
This means that the postfix expression model->set
is sequenced before the expression estimate(model)
in the expression-list. And since model
is null pointer, the precondition of std::shared_ptr::operator->
is violated and hence this leads to UB.