How to create a type-deducing tuple with neither copyable nor movable members? The tuple itself is a class member

2 weeks ago 23
ARTICLE AD BOX

To use std::tuple here, you should not create the S<T> objects too early since they are neither copyable nor movable and copy-elision is also not applicable for this scenario. As has been pointed out by @康桓瑋 a solution is:

auto MakeContainerOfS() { return std::tuple<S<int>, S<float>>(1, 2.0f /*...*/); }

Also you can write this tuple directly as a member:

struct Container { std::tuple<S<int>, S<float>> container{1, 2.0f /*...*/}; };

This uses the values 1 and 2.0f to initialize the S<T> objects in-place, rather than creating ones and passing them into the tuple.

Moreover, there's an interesting way of using multiple inheritance instead of tuple+apply:

template <typename... Types> struct Container : S<Types>... { template <typename... Ts> requires (sizeof...(Ts) == sizeof...(Types)) Container(Ts &&...params) : S<Types>(std::forward<Ts>(params))... {} void Call() { (S<Types>::InterfaceMethod(), ...); } }; template <typename... Ts> Container(Ts &&...) -> Container<std::decay_t<Ts>...>; int main() { Container c{1, 2.0f}; c.Call(); }

Below is the original answer which suggested listing the members and creating a tuple of references. It is simple, though it does require two edits when adding a member.


If the primary aim of creating a tuple is to make use of std::apply to simplify method calling on all members, perhaps just create a tuple of references. It can be either as a member or created locally.

struct Container { S<int> SInt{1}; S<float> SFloat{2.0f}; void Call() { std::tuple container{std::ref(SInt), std::ref(SFloat)}; std::apply([](auto &...members) { (members.get().InterfaceMethod(), ...); }, container); } };
Read Entire Article