【问题标题】:How to make a variadic function of smart pointers?如何制作智能指针的可变参数函数?
【发布时间】:2017-11-23 16:03:08
【问题描述】:

我正在尝试使 一个接口 成为一个抽象类,该类将由派生的孩子实现。该类中的一种方法必须是可变参数(孩子们可以得到一个或多个QSharedPointer<QObject>,具体取决于实现)

问题是:

  1. 模板方法不能是虚拟的

  2. 由于error: expansion pattern ‘QSharedPointer<QObject>’ contains no argument packs.,我无法制作采用QSharedPointer<QObject>... args 参数的可变参数方法

字少,代码多:

class BaseClass {
public:
    virtual void foo(QSharedPointer<QObject>... args) = 0;
}

class ChildClassA : public BaseClass {
public:
    void foo(QSharedPointer<QObject> arg1);
}


class ChildClassB : public BaseClass {
public:
    void foo(QSharedPointer<QObject> arg1, QSharedPointer<QObject> arg2);
}

我想用上面的类来做这样的事情:

template <class T = BaseClass>
class Controller<T>{
    void callFoo(QSharedPointer<QObject>... args){
        T* = new T();
        T->foo(args);
    }
}

如您所见,BaseClass 只是说:使用我的一个孩子作为泛型类型。

如何让这些事情发挥作用?在 C++ 中甚至可能吗?

【问题讨论】:

  • 接收一个智能指针向量怎么样?
  • @Quentin 对我来说有点'meh',因为你可以传递空向量。另外,使用ChildClassA 时每次只将一个对象包装到包装器中是没有意义的
  • 如果你想优化 known-child 访问,你可以保留 N-parameters 函数但是使用一个向量来覆盖虚拟的。问题是当在静态类型为BaseClass 的对象上调用foo 时,C++ 不知道孩子需要多少参数。这也是一种奇怪的设计:怎么知道参数的数量?如果不匹配应该怎么办?将孩子分开是一个解决方案吗?
  • 你总是可以求助于virtual void foo(...) = 0;,但我不知道你是否真的想要。
  • @Quentin 实际上我知道子方法将采用多少个参数,因为通用“控制器”类适用于指定的子类。例如。 template &lt;class T = BaseClass&gt; class GenericClass{} BaseClass 的唯一目的是作为接口,表示必须将 Child 类之一用于该通用插槽。

标签: c++ qt


【解决方案1】:

由于(根据 cmets)您已经通过 arity 将子代分开,并且它们由 Controller 类模板的单独实例化管理,因此尝试将它们全部放入同一个层次结构是没有意义的——这仅意味着解决方法和运行时开销。

相反,让我们将BaseClass 设为模板,并使用其参数生成foo 的签名。

namespace detail {
    template <std::size_t, class T>
    using expandHook = T;
}

template <std::size_t N, class = std::make_index_sequence<N>>
struct BaseClass;

template <std::size_t N, std::size_t... Idx>
struct BaseClass<N, std::index_sequence<Idx...>> {
    virtual void foo(detail::expandHook<Idx, QSharedPointer<QObject>>... args) = 0;
};

detail::expandHook 是一种语法技巧,可以重复 QSharedPointer&lt;QObject&gt; 类型的次数与 Idxes 一样多,这要感谢 std::index_sequence 的工作,即从 0N - 1

那么,children从对应的BaseClass继承:

struct Child : BaseClass<2> {
    void foo(QSharedPointer<QObject>, QSharedPointer<QObject>) override;
};

奖励:如果您希望一个子级支持多个 arities,它可以继承多个 BaseClass 特化!

最后,控制器将通过适当的BaseClass 类型参数化,然后一切都可以点击。

See it live on Coliru(带有存根类型,main 的内容将是 Controller 中的内容)。

【讨论】:

  • 注意:您需要使用C++14或更新版本,2017年的QT​​默认使用C++11 不过,很好的解决方案!谢谢!
  • @Luke ... 和 here's the Q&A 关于在 C++11 中实现 std::index_sequence :)
【解决方案2】:

如果您遵循公共函数应该是非虚拟函数而虚拟函数应该是私有的指导方针,那么您可以通过将基本函数转换为可变参数模板函数来解决这个问题,该函数将委托给采用 @987654321 的虚拟私有函数@。

这还有一个好处是所有的解包都发生在一个地方并且派生类更容易实现。

这是一个示例(我已将 QSharedPtr 替换为 std::shared_ptr 并为 QObject 添加了虚拟实现,以便可以在没有第三方内容的情况下编译该示例):

#include <memory>
#include <vector>
#include <iostream>
#include <cassert>

struct QObject { virtual void sayHi() const = 0; };
struct DerivedQObject1 : public QObject { void sayHi() const override { std::cout << "1\n"; } };
struct DerivedQObject2 : public QObject { void sayHi() const override  { std::cout << "2\n"; } };

class BaseClass {
public:
    template <class... Types>
    void foo(std::shared_ptr<Types>... args)
    {
        std::vector<std::shared_ptr<QObject>> vector;
        pushBack(vector, args...);
        assert(!empty(vector));
        doFoo(vector);
    }

private:
    virtual void doFoo(std::vector<std::shared_ptr<QObject>> const& args) = 0;

    template<typename LastType>
    static void pushBack(std::vector<std::shared_ptr<QObject>>& vector, LastType arg)
    {
        vector.push_back(arg);
    };

    template<typename FirstType, typename ...OtherTypes>
    static void pushBack(std::vector<std::shared_ptr<QObject>>& vector, FirstType const& firstArg, OtherTypes... otherArgs)
    {
        vector.push_back(firstArg);
        pushBack(vector, otherArgs...);
    };
};

class ChildClassA : public BaseClass {
private:
    void doFoo(std::vector<std::shared_ptr<QObject>> const& args) override;
};

void ChildClassA::doFoo(std::vector<std::shared_ptr<QObject>> const& args) {
    for (auto const& arg : args) {
        arg->sayHi();
    }
}

int main() {
    ChildClassA child;
    auto obj1 = std::make_shared<DerivedQObject1>();
    auto obj2 = std::make_shared<DerivedQObject2>();
    child.foo(obj1, obj2);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-22
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 2015-02-25
    • 1970-01-01
    • 2021-03-29
    • 1970-01-01
    相关资源
    最近更新 更多