简体   繁体   中英

C++, pass two parameter packs to constructor

I have the following problem. I have a class ( mixin ), that has two template bases.

template <typename T>
class Id
{
    using result = T;
};

template <typename Printer1, typename Printer2>
class SeveralPrinters : public Printer1, public Printer2
{
    template <typename... Args1, typename... Args2>
    SeveralPrinters(dummy, helper<Args1...>, helper<Args2...>,
                    typename Id<Args1>::result... args1,
                    typename Id<Args2>::result... args2)
        : Printer1(std::forward<Args1>(args1)..., std::forward<Args2>(args2)...)
    {}
public:
    template <typename... Args, typename = 
std::enable_if_t<!contains<dummy, Args...>::result>>
    SeveralPrinters(Args&&... args)
        : SeveralPrinters(dummy(), typename Printer1::ArgsCtor(), 
typename Printer2::ArgsCtor(), std::forward<Args>(args)...)
    {
    }
};

All names of classes are fictitious. So, imagine its first base accepts int as constructor argument and second base accepts double . What I want to do is to be able to call constructor of SeveralPrinters like SeveralPrinters(1, 2.) . The problem here, is that Args1 and Args2 are deduced not from helper structure, but from args, passed after helper structure. As you can see, I tried to wrap template arguments into Id structure, and that didn't help. I know, it is called smth like Non-deduced contexts , but I couldn't manage to make it work. Can anyone help with it (if it possible), and maybe explain a little bit more on this topic (why it doesn't work now). Example of base classes:

class BasicPrinter1
{
public:
    BasicPrinter1(int)
    {}
    void f()
    {
    }
    using ArgsCtor = helper<int>;
};

class BasicPrinter2
{
public:
    BasicPrinter2(int*)
    {}
    void g()
    {
    }
    using ArgsCtor = helper<int*>;
};

It doesn't work mainly because the alias result in Id is private (default accessibility for classes), and so not accessible from the private constructor of SeveralPrinters , leading to a substitution failure ( typename Id<Args1>::result ) with no other viable candidate constructor to call. There were also a couple of typos in your code.

template <typename T>
struct Id
{
    using result = T;
};

template <typename Printer1, typename Printer2>
class SeveralPrinters : public Printer1, public Printer2
{
    template <typename... Args1, typename... Args2>
    SeveralPrinters(dummy, helper<Args1...>, helper<Args2...>
                  , typename Id<Args1>::result... args1
                  , typename Id<Args2>::result... args2)
        : Printer1(std::forward<Args1>(args1)...)
        , Printer2(std::forward<Args2>(args2)...)
    {}

public:    
    template <typename... Args>
    SeveralPrinters(Args&&... args)
        : SeveralPrinters(dummy{}
                        , typename Printer1::ArgsCtor{}
                        , typename Printer2::ArgsCtor{}
                        , std::forward<Args>(args)...)
    {}
};

DEMO


In order to perfectly-forward the arguments to base classes, you should instead declare the number of parameters ( ArgsCount ) and use the below implementation:

template <typename Printer1, typename Printer2>
class SeveralPrinters : public Printer1, public Printer2
{
    template <std::size_t... Is
            , std::size_t... Js
            , typename... Args>
    SeveralPrinters(std::index_sequence<Is...>
                  , std::index_sequence<Js...>
                  , std::tuple<Args...>&& t)
        : Printer1(std::get<Is>(std::move(t))...)
        , Printer2(std::get<sizeof...(Is) + Js>(std::move(t))...)
    {}

public:
    SeveralPrinters() = default;
    SeveralPrinters(const SeveralPrinters&) = default;
    SeveralPrinters(SeveralPrinters& rhs)
        : SeveralPrinters(static_cast<const SeveralPrinters&>(rhs))
    {}

    template <typename... Args>
    SeveralPrinters(Args&&... args)
        : SeveralPrinters(std::make_index_sequence<Printer1::ArgsCount>{}
                        , std::make_index_sequence<Printer2::ArgsCount>{}
                        , std::forward_as_tuple(std::forward<Args>(args)...))
    {}
};

struct BasicPrinter1
{
    BasicPrinter1(int) {}
    static constexpr ArgsCount = 1;
};

struct BasicPrinter2
{
    BasicPrinter2(int*, char&) {}
    static constexpr ArgsCount = 2;
};

DEMO 2

Also notice how I'm protecting the copy-constructor from being overshadowed by the forwarding-references constructor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM