Can I mark a moved variable as no longer usable and receive a compiler warning if I do use it?
Asked Answered
S

2

12

Sometimes in a function I use std::move to pass on a variable I'm no longer using, like this:

void f(std::vector<int> v)
{
    for (int i: v)
    {
        std::cout << i << ", ";
    }
}

void main()
{
    std::vector<int> v(1000);
    std::fill(v.begin(), v.end(), 42);
    f(std::move(v));
}

I understand the std::move leaves my vector in a valid state, so I could call v.clear() and reuse it if I wanted to do so. But in a long function I may later add more code to it and forget that I've lost the data with my move function, introducing a bug.

Is there some kind of compiler instruction I can put after the move to warn me not to reuse this variable? Like this:

void main()
{
    std::vector<int> v(1000);
    std::fill(v.begin(), v.end(), 42);
    f(std::move(v));
    #pragma mark_unusable(v);

    // This should trigger a compiler warning
    v[5] = 10;
}
Serrulate answered 30/9, 2019 at 7:10 Comment(4)
A move operation should not make the object unusable. It should make it "empty", "blank or "null".Viceroy
Instead of marking variables that you don't intend to use anymore, you could try to make your functions shorter or put the related code into an unnamed scope.Faculty
"I understand the std::move leaves my vector in a valid state" - Well, only as far as that it will be valid to destruct it - any guarantees of usability beyond that is up to the implementation of the class in question. Did they document/guarantee that calling .clear() after a move is valid? If not, don't assume that's ok. In any case; how would the compiler know what is valid to do with a type after a move? In short; it cannot know any such thing, so a compiler warning is straight out the window.Plumber
@JesperJuhl From en.cppreference.com/w/cpp/utility/move : "Unless otherwise specified, all standard library objects that have been moved from are placed in a "valid but unspecified state", meaning the object's class invariants hold (so functions without preconditions, such as the assignment operator, can be safely used on the object after it was moved from)". So clear() should be fine, operator[] not fine. It's a confusing example to have chosen in my question - I'll update it.Serrulate
P
8

Partial answer: you can use clang-tidy with its bugprone-use-after-move check. This does not catch v.clear() in your example, but at least other cases instead. Example:

clang-tidy -checks=bugprone-use-after-move your-file.cpp

When you add a second f(std::move(v)); after the first invocation, this gives you

your-file.cpp:15:17: warning: 'v' used after it was moved [bugprone-use-after-move]
f(std::move(v));
            ^
your-file.cpp:14:5: note: move occurred here
f(std::move(v));
^
Playground answered 30/9, 2019 at 7:19 Comment(1)
As far as catching these at compile time goes, this is probably the closest you can reasonably get unless you're willing to do this at runtime. Unfortunately it is clang-specific, but many good things are clang-specific!Brawl
T
1

By no means is this a great answer but you can sometimes (ab)use scopes for this purpose...

void main()
{
    {
        std::vector<int> v(1000);
        std::fill(v.begin(), v.end(), 42);
        f(std::move(v));
    }

    // v is now out of scope so this does not build
    v.clear();
}

Though I do wish there was a way to 'undef' or 'delete' a variable in C++ before it goes out of scope.

Tapping answered 27/10, 2023 at 20:35 Comment(2)
btw. I cannot comment on the one above, but most likely the reason v.clear() doesn't trigger the warning is because it is flagged as 'clang::reinitializes'Huang
I think this is a reasonable use for scopes.Serrulate

© 2022 - 2024 — McMap. All rights reserved.