1

试图重构一些基础设施代码,我决定使用继承来实现多个类之间的多态行为。下一步是将它们放入容器中,遍历它们并 DoSomething();

我目前的容器结构很笨拙。即使我为容器使用这个优雅的解决方案,构造仍然是一团糟。

vector<unique_ptr<Parent>> vec;

vec.push_back(unique_ptr<Parent>(new ChildA(var1, var2)));
vec.push_back(unique_ptr<Parent>(new ChildB(var3, var4)));
vec.push_back(unique_ptr<Parent>(new ChildC(var5, var6)));

ChildA、ChildB 和 ChildC 都是相互多态的。Var1 ... var6 可能来自不同的类型,并且是构建子对象所必需的。

我想在几个地方创建这个向量以供使用。问题是孩子的数量可能会有所不同,因此孩子需要构建的数据。尝试走 stdarg.h 的路径失败,因为不支持:

if     !defined(_WIN32)
#error ERROR: Only Win32 target supported!
#endif

您将如何实现构造此数组的工厂方法?

更新,所需的解决方案:

vec = CreatePolyVec(var1, var2, typeInfo1, var3, var4, typeInfo2, var5, var6 typeInfo3);
vec = CreatePolyVec(var1, var2, typeInfo1, var3, var4, typeInfo2);

第 1 行将创建与上面相同的数组。第 2 行将重用相同的代码来创建一个相似的向量,但对象更少。Typeinfo 保存创建对象所需的信息。

这是一个基本的解决方案,更高级的解决方案将在编译时强制执行参数列表。例如,以下对该函数的调用没有意义:

vec = CreatePolyVec(var1, var2,typeinfo1, var3, var4, typeinfo2, var5);

没有足够的参数来创建最后一个子对象。

4

2 回答 2

4

这里有两个问题,1.如何决定每个孩子将是什么类型,2.如何创建多个孩子。

决定创建哪个孩子

这可以在编译时或运行时完成。要在编译时执行此操作,您需要模板。

template<class Child, class Arg1, class Arg2>
vector<unique_ptr<Parent>> CreateVec(Arg1&& arg1, Arg2&& arg2)
{
    vector<unique_ptr<Parent>> result;
    result.push_back(unique_ptr<Child>(
        new Child(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2))));
    return result;
}

调用如下CreateVec<MyChild>(myArg1, myArg2)

如果您需要在运行时决定,您可以使用由运行时变量索引的工厂函数映射。或者,您可以使用指向工厂对象的指针作为运行时变量。

创建多个孩子

在这里,您可以在链式函数和可变参数模板之间进行选择。

链式函数

这基本上就是 iostreams 所做的。让创建向​​量并添加单个子项的函数返回一个对象,该对象允许您添加第二个子项,并返回自身允许您继续。

这里的问题是你希望函数返回向量,所以它不能也返回另一个对象。您可以使用转换运算符来获取向量或显式函数,但可能最简单的方法是首先创建向量,然后使用函数添加子项。

class AddChildren
{
    vector<unique_ptr<Parent>>& m_vector;
public:
    explicit AddChildren(vector<unique_ptr<Parent>>& theVector)
        : m_vector(theVector) {}

    template<class Child, class Arg1, class Arg2>
    AddChildren& add(Arg1&& arg1, Arg2&& arg2)
    {
        m_vector.push_back(unique_ptr<Child>(
            new Child(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2))));
        return *this;
    }
};

使用如下:

vector<unique_ptr<Parent>> myvector;
AddChildren(myvector)
    .add<ChildA>(var1, var2)
    .add<ChildB>(var3, var4)
    .add<ChildC>(var5, var6);

如果您正在使用运行时方法来选择您可以使用的类型operator()并让它看起来像这样:

vector<unique_ptr<Parent>> myvector;
AddChildren(myvector)
    (childAType, var1, var2)(childBType, var3, var4)(childCType, var5, var6);

(这也可以通过使用每个子类型的特定类型选择器类型的虚拟对象作为参数来在编译时选择类型来完成。)

使用可变参数模板

使用可变参数模板一次剥离三个参数,并添加一个子对象。

void addChildren(vector<unique_ptr<Parent>>& theVector)
{}

template<class FirstChild, class FirstArg1, class FirstArg2, class... Rest>
void addChildren(vector<unique_ptr<Parent>>& theVector,
    FirstChild childtype, FirstArg1&& arg1, FirstArg2&& arg2, Rest&&... rest)
{
    addChild(theVector, childtype,
        std::forward<Arg1>(arg1), std::forward<Arg2>(arg2));
    addChildren(theVector, std::forward<Rest>(rest)...);
}

template<class... Args>
vector<unique_ptr<Parent>> CreateVec(Args&&... args)
{
    vector<unique_ptr<Parent>> result;
    addChildren(result, std::forward<Args>(args)...);
    return result;

}

我在这里假设存在一个函数,该函数addChild可以在给定其类型(作为参数)及其参数的情况下添加一个子项。

主要问题是 VS2012 没有可变参数模板。有两种方法可以模拟可变参数模板。1. 编写一个函数,该函数采用您可能需要的最大参数数量,并将它们中的大多数默认为某种已知类型,您可以将其视为“不存在”。2. 写出你认为需要的尽可能多的重载。

如果你知道你永远不需要超过十个子对象,那么第二个选项实际上是完全可行的——你只需要编写一次它们,它可能少于 150 行代码。或者,您可以使用 Boost.Preprocessor 生成函数,但这是一个全新的复杂程度。

于 2013-01-09T14:47:19.047 回答
2

随着 2012 年 11 月的编译器更新,VS 2012 支持可变参数模板,因此以下应该可以工作(这不是您所追求的语法,但我认为它非常接近):

struct VectorCreator
{
  explicit VectorCreator(std::vector<Parent> &vec)
    : vec_(vec)
  {}

  template <typename Type, typename... ArgType>
  VectorCreator& create(ArgType&&... arg)
  {
    vec_.push_back(std::unique_ptr<Parent>(new Type(std::forward<ArgType>(arg)...));
    return *this;
  }

private:
  std::vector<Parent> &vec_;
};

像这样使用:

std::vector<Parent> vec;
VectorCreator(vec).create<ChildA>(var1, var2).create<ChildB>(var3, var4).create<ChildC>(var5, var6);
于 2013-01-09T14:37:50.017 回答