Is the `this` argument evaluated before or after other member function arguments?

2.2k views Asked by At

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;
}
3

There are 3 answers

13
user12002570 On BEST ANSWER

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.


11
nielsen On

To my understanding this is undefined behavior at least from C++17:

  1. In a function-call expression, the expression that names the function is sequenced before every argument expression and every default argument.

As I interpret this, it actually guarantees that model->set is evaluated before any argument and thus invokes undefined behavior. It does not matter whether or not model is a raw pointer.

0
duck On

[expr.call]/7:

The postfix-expression is sequenced before each expression in the expression-list and any default argument.

In this case, this means that model->set is evaluated before estimate(model).

Since model is a shared_ptr<Model>, model->set uses shared_ptr's overloaded operator->, which has the following precondition ([util.smartptr.shared.obs]/5):

Preconditions: get() != nullptr.

Violating this precondition results in undefined behavior ([structure.specifications]/3.3).