如何制作智能指针的可变参数函数?

How to make a variadic function of smart pointers?

本文关键字:变参 参数 函数 何制作 智能 指针      更新时间:2023-10-16

我正在尝试使接口成为由派生子级实现的抽象类。该类中的方法之一必须是可变参数(根据实现,子项得到一个或多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++甚至可能吗?

由于(根据评论)您已经按 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<QObject>类型,因为有Idxes,由于std::index_sequence的工作,0N - 1

然后,子项从相应的BaseClass继承:

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

奖励:如果您希望孩子支持多种BaseClass专长,则可以从多个专业继承!

最后,控制器将通过适当的BaseClass类型进行参数化,并且一切都点击进入。

在 Coliru 上直播(带有存根类型,main的内容将是Controller内的内容)。

如果您遵循公共函数应该是非虚拟函数而虚拟函数应该是私有的准则,那么您可以通过将基本函数转换为可变参数模板函数来解决此问题,该模板函数委托给std::vector的虚拟私有函数。

这也具有所有解包都发生在一个位置并且派生类更易于实现的优点。

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

#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 << "1n"; } };
struct DerivedQObject2 : public QObject { void sayHi() const override  { std::cout << "2n"; } };
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);
}