How do I forward the values of tuple to a member initializer?

半城伤御伤魂 提交于 2021-01-27 16:28:04

问题


I need to forward the values of a tuple to a member initializer:

struct Struct {
  Member1 member1;
  Member2 member2;

  template<typename Tuple1, typename Tuple2>
    Struct( Tuple1&& tuple1, Tuple2&& tuple2 )
      : member1(tuple1...), member2(tuple2...)
    {}
};

The code above obviously isn't valid. How can I express it?

Member1 and Member2 have no default/copy/move constructor.

I know about std::apply, as suggested in How do I expand a tuple into variadic template function's arguments?. I also know about std::make_from_tuple. But I wouldn't know how to use any of these in a member initializer.

Any C++ standard is fine (preferably C++17, but C++20 would work as well).

To clarify, my real goal is to create a Struct, passing it two sets of variadic arguments to perfect-forward them to initialize member1 and member2. I thought that "grouping" the two sets into tuples could have been a good idea, since that's what std::map::emplace does. Other approaches would work as well (e.g. passing a special object between the two sets of variadic arguments).


回答1:


std::make_from_tuple is indeed the right choice:

#include <tuple>
struct Member1 {
    Member1(int x,float y, char z){}

    Member1(const Member1& other)=delete;
    Member1(Member1&& other)=delete;
};

struct Member2 {
    Member2(int x,float y, char z){}

    Member2(const Member2& other)=delete;
    Member2(Member2&& other)=delete;
};

struct Struct {
  Member1 member1;
  Member2 member2;

  template<typename Tuple1, typename Tuple2>
    Struct(Tuple1&& tuple1, Tuple2&& tuple2)
      : member1(std::make_from_tuple<Member1>(std::forward<Tuple1>(tuple1))),
       member2(std::make_from_tuple<Member2>(std::forward<Tuple2>(tuple2)))
    {}
};

int main(){
    Struct c(std::tuple{1,1.1,'c'},std::tuple{2,2.2,'x'});
}

Godbolt demo.




回答2:


cppreference.com has a nice example of a sample implementation of std::make_from_tuple, however, as you've discovered you can't use it due to the lack of a copy constructor of the underlying class.

However, its breadcrumbs lets you adapt it to work around these limitations:

#include <tuple>
#include <iostream>

struct Member1 {
    Member1(int a, int b)
    {
        std::cout << a << " "
              << b
              << std::endl;
    }

    Member1(const Member1 &)=delete;
    Member1(Member1 &&)=delete;
};

struct Member2 {
    Member2(const char *str)
    {
        std::cout << str << std::endl;
    }

    Member2(const Member2 &)=delete;
    Member2(Member2 &&)=delete;
};

// De-obfucation shortcut

template<typename Tuple>
using make_index_sequence_helper=std::make_index_sequence
    <std::tuple_size_v<std::remove_reference_t<Tuple>>>;

struct Struct {
    Member1 member1;
    Member2 member2;

    template<typename Tuple1, typename Tuple2>
    Struct( Tuple1&& tuple1,
        Tuple2&& tuple2 )
        : Struct{std::forward<Tuple1>(tuple1),
        make_index_sequence_helper<Tuple1>{},
        std::forward<Tuple2>(tuple2),
        make_index_sequence_helper<Tuple2>{}}
    {
    }

    template<typename Tuple1, std::size_t ...tuple1_args,
         typename Tuple2, std::size_t ...tuple2_args>
    Struct(Tuple1 && tuple1,
           std::index_sequence<tuple1_args...>,
           Tuple2 && tuple2,
           std::index_sequence<tuple2_args...>)
        : member1{std::get<tuple1_args>(tuple1)...},
          member2{std::get<tuple2_args>(tuple2)...}
    {
    }
};

int main()
{
    Struct s{ std::tuple<int, int>{2, 3},
        std::tuple<const char *>{"Hello world"}};

    return 0;
}

Tested with gcc 10 with -std=c++17.



来源:https://stackoverflow.com/questions/65443904/how-do-i-forward-the-values-of-tuple-to-a-member-initializer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!