clang-tidy
reports the following piece of code as bugprone-use-after-move
template <typename F, typename Tuple, size_t... I>
auto transform_tuple_impl(F&& f, Tuple&& tuple, std::index_sequence<I...>)
{
return std::make_tuple(f(std::get<I>(std::forward<Tuple>(tuple)))...);
}
template <typename F, typename Tuple>
auto transform_tuple(F&& f, Tuple&& t)
{
constexpr size_t size = std::tuple_size<std::decay_t<Tuple>>::value;
return transform_tuple_impl(std::forward<F>(f), std::forward<Tuple>(t), std::make_index_sequence<size>{});
}
With the this message:
error: 'tuple' used after it was forwarded [bugprone-use-after-move,-warnings-as-errors]
| return std::make_tuple(f(std::get<I>(std::forward<Tuple>(tuple)))...);
| ^
note: forward occurred here
| return std::make_tuple(f(std::get<I>(std::forward<Tuple>(tuple)))...);
| ^
note: the use and forward are unsequenced, i.e. there is no guarantee about the order in which they are evaluated
| return std::make_tuple(f(std::get<I>(std::forward<Tuple>(tuple)))...);
| ^
I'm using C++14 so std::get
should have the overloaded operator for Rvalue reference. Would there be a way to enforce the order of evaluation? Is this a false-positive?
For an actual std::tuple
this is safe, because of the exact specification of std::get
for std::tuple
and the fact that you std::get
and pass on each tuple element exactly once.
But for other types Tuple
it may not be and if you didn't pass a proper index sequence to the function (e.g. one with repeating indices), then the diagnostic would be correct as well.
You are not constraining Tuple
to be a std::tuple
and even if you did I have some doubts that clang-tidy's check is written clever enough to take that constraint and the special known behavior of std::get
for std::tuple
into account. Also, the behavior is only correct given that a proper index sequence is passed, which is also probably too complex of an analysis to make for clang-tidy.
Order of evaluation isn't really all that relevant. The main problem that clang-tidy reports is either way that you pass std::forward<Tuple>(tuple)
multiple times to functions, regardless of order. Which in general is wrong and only correct in special cases where move semantics of a type/function are specified more strictly than usual.