men*_*oom 33 c++ struct sequence-points language-lawyer
在下面的代码中,在set()a 上调用一个成员函数model,它是一个空指针。这将是未定义的行为。然而,成员函数的参数是另一个函数调用的结果,该函数调用检查是否为model空指针并在这种情况下抛出异常。是否保证estimate()总是在访问之前被调用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;
}
Run Code Online (Sandbox Code Playgroud)
use*_*570 28
根据expr.compound ,这仍然是未定义的行为(UB) :
后缀表达式 按顺序排列在表达式列表中的每个表达式和任何默认参数之前。参数的初始化(包括每个关联的值计算和副作用)相对于任何其他参数的初始化是不确定的。
(强调我的)
这意味着后缀表达式在表达式列表中的model->set表达式之前排序。estimate(model)由于是空指针,因此违反了model的前提条件,因此导致 UB。std::shared_ptr::operator->
nie*_*sen 10
据我了解,这至少在 C++17 中是未定义的行为:
- 在函数调用表达式中,命名函数的表达式按顺序排列在每个参数表达式和每个默认参数之前。
当我解释这一点时,它实际上保证model->set在任何参数之前评估 ,从而调用未定义的行为。是否是model原始指针并不重要。
后缀表达式按顺序排列在表达式列表中的每个表达式和任何默认参数之前。
在这种情况下,这意味着model->set之前已评估estimate(model)。
由于model是 a shared_ptr<Model>,因此model->set使用shared_ptr的重载operator->,它具有以下前提条件([util.smartptr.shared.obs]/5):
前提条件:
get() != nullptr.
违反此前提条件会导致未定义的行为([struct.species]/3.3)。