Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialize tuple using user defined constructor without moving

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?

like image 449
king_nak Avatar asked Jun 04 '26 17:06

king_nak


2 Answers

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

like image 141
Jarod42 Avatar answered Jun 07 '26 08:06

Jarod42


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};
}
like image 43
463035818_is_not_a_number Avatar answered Jun 07 '26 06:06

463035818_is_not_a_number