I would like to move the elements out of a class member tuple-type variable, but I wonder whether the following code (near-minimal example, being limited to C++14) is allowed:
#include <tuple>
#include <iostream>
// For testing, assume a move-only class template:
enum class Index { idx1, idx2, idx3 };
template <Index I>
struct B {
~B() = default;
B(B&&) = default;
B& operator= (B&&) = default;
B(B const&) = delete;
B& operator= (B const&) = delete;
Index getIndex() { return I; }
};
// Class to consider
template <Index... Is_>
class A
{
public:
A(B<Is_>&&... _b) : m_tuple{std::move(_b)...} {}
template <Index... Js_>
std::tuple<B<Js_>...> moveBs() &&
{
return { std::move(*this).template moveB<Js_>()... };
}
private:
template <Index J_>
B<J_> moveB() &&
{
// Attention: The entire tuple shall be moved, not the return
// value(s) of std::get<>() - see answer to question
// 77664807: "How does std::forward work in the
// context of a fold expression?", thanks to
// 'user12002570' for pointing this out.
return std::get<B<J_>>(std::move(m_tuple));
}
std::tuple<B<Is_>...> m_tuple;
};
// Output only as an example, to try out
void print(Index _i)
{
std::cout << ( (_i == Index::idx1) ? "Index::idx1"
: (_i == Index::idx2) ? "Index::idx2"
: (_i == Index::idx3) ? "Index::idx3"
: "unknown!")
<< std::endl;
}
int main()
{
A<Index::idx1,
Index::idx2,
Index::idx3> a{ B<Index::idx1>{},
B<Index::idx2>{},
B<Index::idx3>{} };
B<Index::idx1> b1{};
B<Index::idx2> b2{};
// Call to consider
std::tie(b1, b2) = std::move(a).moveBs<Index::idx1,
Index::idx2>();
// Usage of objects b1,b2 - verify the result of the previous call
print(b1.getIndex());
print(b2.getIndex());
}
The question referred to in the example code is found here
Clang and GCC compilers accept this code, but moving several times from object a
, as it is done through moveBs<Js_>()
, feels illegal.
Compiler options used: --std=c++14 -O3 -Wall -Wextra -Wshadow -Wconversion -Wpedantic -Werror
.
Is it allowed to move from a member like m_tuple
if that is done from a &&
-qualified method like moveB<>()
?
Is it allowed to "invoke" std::move(*this)
multiple times, as it seems to be done in the parameter pack expansion in moveBs<>()
?
Is it allowed to move from a member like
m_tuple
if that is done from a&&
-qualified method likemoveB<>()
?
Yes.
Is it allowed to "invoke"
std::move(*this)
multiple times, as it seems to be done in the parameter pack expansion inmoveBs<>()
?
Yes.
What you do not want to do is to move multiple times from a resource that may end up in a valid but indeterminate state. Your code doesn't do that.