在下面的代码中,在model
上调用成员函数set()
,它是一个空指针。这将是未定义的行为。但是,成员函数的参数是另一个函数调用的结果,该函数调用检查model
是否为空指针并在这种情况下抛出。是否保证在访问model
之前始终调用估计()
,或者它仍然是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;
}
这仍然是未定义的行为(UB),根据expr.化合物:
后缀表达式在表达式列表和任何默认参数中的每个表达式之前排序。参数的初始化,包括每个相关的值计算和副作用,相对于任何其他参数的初始化是不确定的。
(强调我的)
这意味着postfix表达式模型-
此外,expr. ref还可以用来查看这是UB:
后缀表达式后跟一个点。或箭头-
(强调我的)
这意味着评估箭头之前的后缀表达式model
以及该评估的结果(即nullptr
)与id-表达式操作符-
据我了解,这至少从C 17开始是未定义的行为:
当我解释这一点时,它实际上保证了模型-
[expr.call]/7:
postfix表达式在表达式列表中的每个表达式和任何默认参数之前排序。
在这种情况下,这意味着模型-
由于model
是一个shared_ptr
前置条件:获取() ! = nullptr。
违反此先决条件会导致未定义的行为([structure.规范]/3.3)。