In following code std::move
in lambda capture list felt unnecessary to me, but compiler does seem to need it.
As there is extra code for copying shared_ptr is generated if I don't use std::move
.
Question is, why compiler can't optimise this on its own.
template<typename T>
std::function<void(void)> prepLambdaImpl(std::shared_ptr<T> aptr) {
#ifdef CONVERT_SHARED_PTR_TO_XVALUE
return [aptr=std::move(aptr)]
#else
return [aptr]
#endif
{
printf("use count: %ld\n", aptr.use_count());
};
}
Working example: https://godbolt.org/z/W3oWEjsjK
CodePudding user response:
Without std::move
the std::shared_ptr
will be copied into the lambda. Copying (but not moving) a std::shared_ptr
requires incrementing its atomic reference count (and decrementing it again once the copy is destroyed).
In theory a compiler would be allowed to remove the atomic increment from the copy and the atomic decrement from the aptr
parameter's destructor since they cancel, assuming it makes sure that no other thread can observe an intermediate state, however compilers tend to be conservative and don't optimize atomic operations because that is often not what users except to happen.
CodePudding user response:
Automatic move only happens in return statement in some specific circumstances, which are not met here.
Last usage doesn't trigger a move instead of a copy.
The as-if rule allows optimization as long than observable behaviour is unchanged. Whereas we know the 2 forms are equivalent, it is not easy to know. Especially move and copy are unrelated functions; so optimizers only uses the copy code to try to optimize it (and it probably misses some extra information as invariant of the class (as refcounter not 0 at start of function)).