“this”参数是在其他成员函数参数之前还是之后求值?

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->


  • @mentalmushroom 您对 `std::shared_ptr` 的使用使得这一点非常明确,因为 `std::shared_ptr::operator-&gt;` 重载每个库规范都有一个前提条件,即指针不为空。因此,无论“-&gt;set”对结果的评估如何,对 _that_ 函数的调用已经具有未定义的行为。对于原始空指针是否同样如此是一个不同的问题。 (6认同)
  • @mentalmushroom 如果“set”是“virtual”,则必须实际取消引用指针以根据对象的动态类型分派调用。标准中没有单独指出这种情况(即使非“虚拟”函数*可以*仅根据“模型”的静态类型进行分派),因此一般情况是指针*必须*是可解引用的。 (3认同)

nie*_*sen 10

据我了解,这至少在 C++17 中是未定义的行为:

  1. 在函数调用表达式中,命名函数的表达式按顺序排列在每个参数表达式和每个默认参数之前。

当我解释这一点时,它实际上保证model->set在任何参数之前评估 ,从而调用未定义的行为。是否是model原始指针并不重要。

  • @Val这是cppreference,而不是标准。本文是为了可读性而编写的,而不是为了最终的精确性。[实际规则](https://timsong-cpp.github.io/cppwp/n4868/expr.compound#expr.call-8)表示*后缀表达式*在参数初始化之前进行排序。从 cppreference 引用的规则确实适用于此。 (5认同)
  • @Val即使在标准中,我也相信“函数”一词涵盖了成员函数和非成员函数,并且在适用时进行了明确的区分。一般来说,“成员函数调用”是“函数调用”的特例。 (4认同)
  • 函数调用和成员函数调用是两个不同的东西。 (2认同)

duc*_*uck 7

[expr.call]/7 :

后缀表达式按顺序排列在表达式列表中的每个表达式和任何默认参数之前。

在这种情况下,这意味着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)。