Home > OS >  How to unroll a std::tuple<...> when calling a constructor
How to unroll a std::tuple<...> when calling a constructor

Time:02-17

I've written a class shrink_pair which does the same like pair but first and second are [[no_unique_address]].

I want to write a similar constructor like std::pair's piecewise_construct constructor. This constructor should call first and second's constructors by unpacking the first and second tuple and doing forward()-ing of the tuples elements.

std::apply doesn't seem to work for me since I'm not calling a normal callable object but a constructor. How can I do that?

CodePudding user response:

I believe this is what std::make_from_tuple(C 17) is for:

#include <tuple>

struct Foo {
    Foo(int x, float y) {}
};
struct Bar {
    Bar(int x, float y) {}
};

template <typename T, typename U>
struct Pair {
    template <typename TupleT, typename TupleU>
    Pair(TupleT&& first, TupleU&& second)
        : first(std::make_from_tuple<T>(std::forward<TupleT>(first))),
          second(std::make_from_tuple<U>(std::forward<TupleU>(second))) {}

    T first;
    U second;
};

int main() { Pair<Foo, Bar> p{std::tuple{1, 1.2f}, std::tuple{2, 2.0f}}; }

CodePudding user response:

For C 17 onwards, see @Quimby's answer.

For C 11 and C 14, you need to do it the old way with std::index_sequence:

template <class U, class V>
class custom_pair {
    template <std::size_t... UIs, std::size_t... VIs, class... UArgs, class... VArgs>
    custom_pair(
        std::index_sequence<UIs... >, std::tuple<UArgs... > u_args,
        std::index_sequence<VIs... >, std::tuple<VArgs... > v_args) :
        first(std::forward<UArgs>(std::get<UIs>(u_args))... ), 
        second(std::forward<VArgs>(std::get<VIs>(v_args))... ) { }

public:
    U first;
    V second;

public:

    template <class... UArgs, class... VArgs>
    custom_pair(std::piecewise_construct_t,
        std::tuple<UArgs... > u_args,
        std::tuple<VArgs... > v_args) :
        custom_pair(
            std::make_index_sequence<sizeof... (UArgs)>{}, std::move(u_args),
            std::make_index_sequence<sizeof... (VArgs)>{}, std::move(v_args)) { }
};

The standard requires the std::pair equivalent constructor to work for non-moveable type, so you cannot use intermediate function (such as a custom std::make_tuple) in C 14, you have to unpack in the initializer-list of your custom pair constructor.

In C 17, you can use intermediate function such as std::make_tuple since you get guaranteed copy-elision, as in @Quimby's answer.


Full example on godbolt: https://godbolt.org/z/7PsEjs18r

  •  Tags:  
  • c
  • Related