I have a situation where I have a templated class that contains a tuple. The elements must be initialized (they all have compatible constructor), but cannot be moved.
Simplified code showing the problem:
#include <tuple>
struct A {
int i{};
A(int i) : i(i) {}
// This causes a compile error in the tuple constructor:
A(A&&) = delete;
};
struct B {
int i{};
B(int i) : i(i) {}
B(B&& b) : i(b.i) {}
};
template <typename... Fs>
struct Container
{
std::tuple<Fs...> fields;
constexpr Container(int i) : fields(Fs{i}...) {}
};
int main()
{
Container<A, B> c{1};
}
However, when using a tuple directly, the move c'tor is not used. Instead I need a default constructor for the elements. This is however not possible in my situation. Also note that I cannot make the constructors of my elements constexpr
Now my main concern is calling a constructor on all elements when initializing a tuple. Is it possible to do this without a move constructor?
You might use a "dummy" template type which allows expansion as std::void_t or std::type_identity:
template <typename... Fs>
struct Container
{
std::tuple<Fs...> fields;
constexpr Container(int i) : fields((std::void_t<Fs>(), i)...) {}
};
Demo with std::void_t/
Demo with std::type_identity
You don't have to move anything. You just have to pass i to the constructor of the tuple, which will then forward the arguments to the elements constructors. The tricky part is to get i n-times.
Your code does that via Fs{i}... but that incurs the unnecessary moves because you are first constructing the tuples elements then pass them to the tuples constructor when you could just pass i directly to the tuple's constructor. You can do that eg by using a index_sequence:
#include <tuple>
#include <cstddef>
#include <utility>
struct A {
int i{};
A(int i) : i(i) {}
A(A&& a) = delete;
};
struct B {
int i{};
B(int i) : i(i) {}
B(B&& b) = delete;
};
template <typename... Fs>
struct Container
{
std::tuple<Fs...> fields;
constexpr Container(int i) : Container(i,std::make_index_sequence<sizeof...(Fs)>()) {}
template <size_t... ints>
constexpr Container(int i, std::index_sequence<ints...> int_seq)
: fields((void(ints),i)...) {}
};
int main()
{
Container<A, B> c{1};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With