如何在给定任意数量的参数的情况下创建构造对象的工厂函数模板?

时间:2012-01-05 20:38:03

标签: c++

使用某些代码更容易解​​释,所以我先给出一个例子:

#include <iostream>
#include <vector>

class Base {
public:
    int integer;

    Base() : integer(0) {}
    Base(int i) : integer(i) {}
};

class Double: public Base {
public:
    Double(int i) { integer = i * 2; }
};

class Triple: public Base {
public:
    Triple(int i) { integer = i * 3; }
};

template<typename T>
Base* createBaseObject(int i) {
    return new T(i);
};

int main() {
    std::vector<Base*> objects;

    objects.push_back(createBaseObject<Double>(2));
    objects.push_back(createBaseObject<Triple>(2));

    for(int i = 0; i < objects.size(); ++i) {
        std::cout << objects[i]->integer << std::endl;
    }

    std::cin.get();
    return 0;
}

我正在尝试创建一个函数,该函数将返回一个Base指针,该指针指向一个派生自Base的对象。在上面的代码中,函数createBaseObject允许我这样做,但它限制了我,因为它只能创建将一个参数带入其构造函数的衍生类。

例如,如果我想创建一个派生类Multiply:

class Multiply: public Base {
public:
    Multiply(int i, int amount) { integer = i * amount; }
};

createBaseObject将无法创建Multiply对象,因为它的构造函数需要两个参数。

我想最终做这样的事情:

struct BaseCreator {
    typedef Base* (*funcPtr)(int);
    BaseCreator(std::string name, funcPtr f) : identifier(name), func(f) {}

    std::string identifier;
    funcPtr func;
};

然后,例如,当我得到匹配identifier的输入时,我可以创建一个新对象,该任何派生类与该标识符关联,并输入任何参数,并将其推送到容器。


在阅读了一些回复之后,我认为这样的事情符合我的需要,能够在程序上创建一个对象的实例?我对模板并不太明智,所以我不知道这是否合法。

struct CreatorBase {
    std::string identifier;
    CreatorBase(std::string name) : identifier(name) {}

    template<typename... Args>
    virtual Base* createObject(Args... as) = 0;
};

template<typename T>
struct Creator: public CreatorBase {
    typedef T type;

    template<typename... Args>
    Base* createObject(Args... as) {
        return new type(as...);
    }
};

好的,这是迄今为止我设法提出的另一个半解决方案:

#include <boost\lambda\bind.hpp>
#include <boost\lambda\construct.hpp>
#include <boost\function.hpp>

using namespace boost::lambda;
boost::function<Base(int)> dbl = bind(constructor<Double>(), _1);
boost::function<Base(int, int)> mult = bind(constructor<Multiply>(), _1, _2);

这与原版有相同的限制,因为我没有一个指向dblmult的指针。

3 个答案:

答案 0 :(得分:5)

C ++ 11 variadic templates可以为你做到这一点。

您已经拥有了新的派生类:

class Multiply: public Base {
public:
    Multiply(int i, int amount) { integer = i * amount; }
};

然后改变你的工厂:

template<typename T, typename... Args>
Base* createBaseObject(Args... as) {
    return new T(as...);
};

最后,允许推断出论点:

objects.push_back(createBaseObject<Multiply>(3,4));

Live demo.


正如其他人所说,但这一切似乎都毫无意义。大概你的真实用例不那么做作。

答案 1 :(得分:3)

为什么不提供带有模板化参数的多次重载?

template<typename TBase, TArg>
Base* createBaseObject(TArg p1) {
    return new TBase(p1);
};

template<typename TBase, TArg1, TArg2>
Base* createBaseObject(TArg p1, TArg2 p2) {
    return new TBase(p1, p2);
};

答案 2 :(得分:1)

使用可变参数模板:

template <typename R, typename ...Args>
Base * createInstance(Args &&... args)
{
    return new R(std::forward<Args>(args)...);
}

用法:objects.push_back(createInstance<Gizmo>(1, true, 'a'));

有点难以理解为什么你会想要这个,不过,你可能只是说:

objects.push_back(new Gizmo(1, true, 'a'));

更好的方法是声明向量带有std::unique_ptr<Base>个元素。