C++,将两个参数包传递给构造函数

4
我有一个问题。我有一个类(mixin),它有两个模板基础。
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)...)
    {
    }
};

所有类名都是虚构的。因此,想象它的第一个基类接受int作为构造函数参数,而第二个基类接受double。我想做的是能够像SeveralPrinters(1, 2.)这样调用SeveralPrinters的构造函数。问题在于,Args1Args2不是从helper结构中推导出来的,而是从helper结构之后传递的args中推断出来的。正如您所看到的,我试图将模板参数包装到Id结构中,但这并没有帮助。我知道,这被称为非推导上下文,但我无法使其工作。有人可以帮忙吗(如果可能的话),并可能对这个主题进行更详细的解释(为什么现在不起作用)。 基类示例:

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

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

你不能从单个参数列表中推断出两个或更多的参数包。就是这样。你必须选择不同的调用顺序。也许可以使用一对元组。 - Sam Varshavchik
3
OP正在使用helper结构体来推断它们。 - Piotr Skotnicki
1个回答

3
主要是因为Id中的别名result私有的(类的默认访问控制),因此在SeveralPrinters的私有构造函数中无法访问,导致替换失败(typename Id<Args1>::result)且没有其他可行的构造函数可调用。您的代码中还有几个错别字。
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)...)
    {}
};

为了完美地将参数转发到基类,您应该声明参数数量(ArgsCount),并使用以下实现方式:

演示


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;
};

演示2

同时请注意,我正在保护复制构造函数不被转发引用构造函数所掩盖。


我想第一个构造函数中的args1args2也是通用引用。 - max66
真遗憾 :) 谢谢! 编译器错误甚至没有指出问题是由于私有使用。另外,有趣的是,这段代码会导致clang无限编译并吃掉所有的RAM,尽管g++可以编译它而没有问题。如果我添加enable_if,clang仍然会说Args1为空,就好像没有Id结构一样。 - Nikita Vorobyev
@NikitaVorobyev 我已经删除了你的 contains 谓词,在这种情况下,它可能会导致无尽的递归调用,因为转发引用会生成精确匹配。 - Piotr Skotnicki
@max66 在这种情况下进行完美转发需要更多的努力,因为 Args1Args2 不能是转发引用(尽管可以优化此实现,例如将参数作为元组转发并提取每个构造函数调用的确切元素数量)。 - Piotr Skotnicki
@PiotrSkotnicki,这就是我想问的问题。如何完美地转发Args? - Nikita Vorobyev
@PiotrSkotnicki - 你说得对...这更加复杂了。不错的更新。不幸的是我不能再点赞了 :( - max66

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接