在下面的代码中,一个成员函数set()
在一个model
上被调用,这是一个空指针。这将是未定义的行为。然而,成员函数的参数是另一个函数调用的结果,该函数调用检查model
是否为空指针,并在这种情况下抛出。是否保证estimate()
总是在访问model
之前被调用,或者它仍然是未定义行为(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;
}
3条答案
按热度按时间ubbxdtey1#
根据表达式化合物,这仍是未定义行为(UB):
postfix-expression
在expression-list
和任何默认参数中的每个expression
之前排序。参数的初始化,包括每个相关的值计算和副作用,相对于任何其他参数的初始化顺序不确定。(强调我的)
这意味着后缀表达式
model->set
在表达式列表中排序在表达式estimate(model)
之前。由于model
是空指针,std::shared_ptr::operator->
的前提条件被违反,因此这导致UB。a1o7rhls2#
根据我的理解,这是未定义的行为at least from C++17:
1.在函数调用表达式中,命名函数的表达式在每个参数表达式和每个默认参数之前排序。
正如我所解释的,它实际上保证了
model->set
在任何参数之前被求值,因此调用了未定义的行为。model
是否是原始指针并不重要。bvjveswy3#
[expr.call]/7:
在这种情况下,这意味着
model->set
在estimate(model)
之前被评估。由于
model
是一个shared_ptr<Model>
,model->set
使用shared_ptr
的重载operator->
,它有以下前提条件([util.smartptr.shared.obs]/5):get() != nullptr
。违反这个前提条件会导致未定义的行为([structure.specifications]/3.3)。