Home > Net >  Is clang-tidy (version 14) right with `bugprone-use-after-move` error on this code?
Is clang-tidy (version 14) right with `bugprone-use-after-move` error on this code?

Time:09-16

struct test {
    test& foo(std::vector<int> const& v) {
        assert(v.size() == 1); return *this; 
    }

    void bar(std::vector<int> v) {}
};

void do_test() {
    std::vector<int> v = { 42 };
    return test{}.foo(v).bar(std::move(v)); // <-- here
}

clang-tidy gives an error bugprone-use-after-move on "here" string

Why it is use after move?

CodePudding user response:

Since C 17 it is guaranteed that the postfix-expression in a function call, i.e. here test{}.foo(v).bar is evaluated before any expression in the argument list.

Therefore the move-construction will happen only after foo has returned.

However, before C 17 there was no such guarantee and the move-construction might have happened before the call to foo.

It is questionable whether it is a good idea to rely on these new guarantees. Some tools tend to ignore them and continue warning about such code since it would be dangerous if compiled with a different -std= flag. GCC does for example the same with its -Wunsequenced warnings. There is also the risk that some older compiler versions may still have had bugs in that regard when C 17 was just implemented.

CodePudding user response:

In

foo(v).bar(std::move(v))

There are no guarantees(See comments, pre C 17) that the v in foo(v) will be evaluated before the std::move(v) in bar(std::move(v))

The clang-tidy docs comes with an example of this.

Unsequenced moves, uses, and reinitializations

In many cases, C does not make any guarantees about the order in which sub-expressions of a statement are evaluated. This means that in code like the following, it is not guaranteed whether the use will happen before or after the move:

void f(int i, std::vector v); std::vector v = { 1, 2, 3 }; f(v[1], std::move(v));

In this kind of situation, the check will note that the use and move are unsequenced.

The check will also take sequencing rules into account when reinitializations occur in the same statement as moves or uses. A reinitialization is only considered to reinitialize a variable if it is guaranteed to be evaluated after the move and before the use.

  • Related