Let’s use an expensive class Trace.
It’s produced by the produce
factory function and we want to pass it to consume
function for further processing.
Since we no longer need its instance after it’s consumed and developers conveniently implemented super cheap move constructor, we can leverage the power of std::move and avoid expensive copies.
And, of course, we are using const everywhere. After all that’s what we are always supposed to do, right? Sounds like a victory and time to call it day, right? But just before closing compiler explorer tab, we notice one something suspicious
note the type of the consume
function parameter T got resolved to - it’s Trace const&&! This means that instead of move, super expensive copy constructor is going to be used.
To better understand why this happens, we can write our own version of std::move
Note that const modifier is not removed which prevents const values from being moved. Unfortunately, compilers do not warn about this even though it’s very likely that this is not what developers had in mind when using std::move. Well, now that we have our own implementation, why don’t we “fix” this compiler omission?
Now the following snippet
results in a compiler error
Alternatively it would be nice to have a clang-tidy check or any other linter check to flag these issues without having to use custom std::move implementation, but on the bright side, this type of check happens as part of compilation and doesn’t require any external tools.
There does exist a clang-tidy check for this, performance-move-const-arg: https://releases.llvm.org/10.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/performance-move-const-arg.html
It also checks for when the argument is trivially copyable, where a std::move() would be a no-op.