3

I have read this, this, this and many others... but none of these posts answers or is applicable to my specific problem.

I have a struct X with a variadic template constructor:

struct X
{
    template<typename... T>
    X(T... t)   { /*...*/ }
};

And I have a structure Y that contains two objects of type X. I would like to define a template constructor for Y, allowing to properly initialize both members of type X with distinct parameters list, ie something that looks like the following code (which obviously does not work):

struct Y
{
    template<typename... U, typename... V>
    Y(U&&... u, V&&... v)                                // this does not work
        : x1(std::forward(u)...), x2(std::forward(v)...) // this is was I need to do
        {}

    X x1, x2;
};

How could I do that, using wrappers, tuples or any suitable metaprogramming machinery ? A C++14 solution is acceptable.

Community
  • 1
  • 1
shrike
  • 4,449
  • 2
  • 22
  • 38
  • @πάντα ῥεῖ: I am asking for C++14 ; the question you linked may be a suitable solution to my problem but it is 2 years old ! maybe there is a better one using C++14. – shrike Jul 29 '16 at 16:51
  • I'm not so sure that c++14 changed much about the unpacking of VTPs, but well ... – πάντα ῥεῖ Jul 29 '16 at 16:53
  • @πάντα ῥεῖ: you are not sure ? I am quite sure C++14 added some new interesting features for unpacking tuples... – shrike Jul 29 '16 at 16:54
  • Oh, you re-tagged the question now, so I couldn't reopen it single handedly, sorry. But that gives a chance that re-opening is at least confirmed by 4 others. Gave you an upvote, to attract more reviewers. – πάντα ῥεῖ Jul 29 '16 at 16:55
  • @πάντα ῥεῖ: Maybe you should consider taking more time before flagging a question. In the last 4 hours, 2 over 3 flagged questions in the C++ tag are flagged by you. Don't take offense, just think about it. – shrike Jul 29 '16 at 20:45
  • Well, most of the time I'm right about it (and it were even more). Yours was an unfortunate incident, so I'm actually sorry as mentioned. – πάντα ῥεῖ Jul 29 '16 at 21:04
  • @Barry Any opinion at this case? I just duped trusting nathan, but neither me or he were actually sure about that dupe. – πάντα ῥεῖ Jul 29 '16 at 21:11
  • 1
    @πάνταῥεῖ I voted to reopen as I kind of think the question and the answers are confusing at best. – Barry Jul 29 '16 at 21:23
  • @Barry It was that unfortunate removal of the c++ tag then, well I already apologized for that premature closing. – πάντα ῥεῖ Jul 29 '16 at 21:29

2 Answers2

3

Bog-standard index_sequence trick.

struct Y
{
private:
    template<typename... U, typename... V,
             std::size_t... UIs, std::size_t... VIs>
    Y(std::tuple<U...>&& u, std::tuple<V...>&& v,
      std::index_sequence<UIs...>, std::index_sequence<VIs...>)
        : x1(std::get<UIs>(std::move(u))...), 
          x1(std::get<VIs>(std::move(v))...)  
        {}
public:
    template<typename... U, typename... V>
    Y(std::tuple<U...> u, std::tuple<V...> v)
        : Y(std::move(u), std::move(v),
            std::index_sequence_for<U...>{},
            std::index_sequence_for<V...>{})
        {}

    X x1, x2;
};

In C++17, just use make_from_tuple:

struct Y
{
public:
    template<typename... U, typename... V>
    Y(std::tuple<U...> u, std::tuple<V...> v)
        : x1(std::make_from_tuple<X>(std::move(u))),
          x2(std::make_from_tuple<X>(std::move(v)))
        {}

    X x1, x2;
};
T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Thank you very much, your trick works well... unless I want to pass references to inner objects `x1` & `x2`; (I tried using `std::forward_as_tuple()` but this did not work.) Is there any way to modify your code so that it can work with tuples of references ? (I updated my question in order to show the perfect forwarding I need.) – shrike Jul 30 '16 at 09:07
  • There shouldn't be a need for any modification. – T.C. Jul 30 '16 at 09:13
  • That's right ! I made mistake on the parameters list. Your trick works perfectly well for references too. Thanks a lot. – shrike Jul 30 '16 at 10:18
0

Using a tuple is a big overhead, since it already require from X to be moveable/copyable, you can use that constraint directly, and get the most readable code ever:

struct Y
{
    Y(X && _x1, X && _x2)                        
        : x1(std::move(_x1)), x2(std::move(_x2)) 
        {}

    X x1, x2;
};

And in code just write:

Y y(X(args_x1...), X(args_x2...));

Or if X has implicit constructor:

Y y({args_x1...}, {args_x2...});

SampleCode

The more interesting problem is, what if X is not moveable/copyable, but this is out of the scope ;)

jenkas
  • 872
  • 14
  • 16