如何使用可变参数模板概括C ++中的对象创建?

时间:2019-01-04 15:13:59

标签: c++ templates variadic-templates generic-programming

我正在尝试用C ++创建一个通用工厂方法,该方法可以创建许多(但数量有限)对象之一的实例。每个对象都需要不同类型的参数来构造,因此我希望该方法以某种方式推断所需的类型,而不希望用户明确指定它。

这里有一些代码来说明我正在尝试做的事情:

#include <iostream>
using namespace std;

class A {
public:
    A(int x, int y) {
        cout << "A(" << x << ", " << y << ")\n";
    }
};

class B {
public:
    B(float a, float b) {
        cout << "B(" << a << ", " << b << ")\n";
    }
};

template<typename T, typename... Ts>
T * Make(Ts... vs) {
    puts(__PRETTY_FUNCTION__); // __FUNCSIG__ or __PRETTY_FUNCTION__
    return new T{ vs... };
}

现在,main函数中的代码可以通过调用A *来创建类型为B *Make的对象,如下所示:

A *a = Make<A>(3, 4);
B *b = Make<B>(3.14f, 6.28f);

有没有一种方法可以扩展此代码,以使其他函数可以调用Make,而不必显式指定它们是否需要A *B *的实例?例如,

A * a = Make(3, 4); // (int, int)
B * b = Make(3.14f, 6.28f); // (float, float)

我了解使用参数类型推导实例化函数模板,并且不涉及返回类型。但是,编译器将不执行任何类型转换。因此Make(int, int)绝对是与Make(float, float)不同的实例,我希望能够利用它来将函数定义映射到正确的返回类型。

这是我尝试过的:

  • 定义一个显式实例化

    template A * Make(int x, int y);
    
  • 定义专业化

    template<>
    A * Make<A, int, int>(int x, int y);
    

两者均未按预期工作。关于如何实现的任何想法?

2 个答案:

答案 0 :(得分:3)

我写了一个Maker帮助程序模板,注册了允许使用的类,尽管我并不完全知道如何禁用基本模板:

template <typename... Ts>
struct Maker {
    using type = void;
};

template <>
struct Maker<int, int> {
    using type = A;
};

template <>
struct Maker<float, float> {
    using type = B;
};

template<typename... Ts, typename T=typename Maker<Ts...>::type>
T * Make(Ts... vs) {
    puts(__PRETTY_FUNCTION__); // __FUNCSIG__ or __PRETTY_FUNCTION__
    return new T{ vs... };
}

int main() {
    A * a = Make(3, 4); // (int, int)
    B * b = Make(3.14f, 6.28f); // (float, float)
}

答案 1 :(得分:3)

您可能会向工厂提供所需的信息,要构造的类集以及所需的参数(*)。

(*):(注意:如果您的类具有正确的属性,您甚至可以使用magic_get自动检测构造函数的参数。

template <typename ... Sigs> struct Maker : Maker<Sigs>...
{
    using Maker<Sigs>::operator ()...;

    // Do we want to accept conversions or not in non ambiguous cases ?
    //template <typename ... Ts> operator()(Ts/*&&*/...) const = delete;
};

template <class C, typename ... Args> struct Maker<C(Args...)>
{
    C operator()(Args... args) const {
        puts(__PRETTY_FUNCTION__); // __FUNCSIG__ or __PRETTY_FUNCTION__
        return C(std::forward<Args>(args)...); // not {} to avoid initializer_list constructor
    }
};

然后是工厂实例:

constexpr Maker<A(int, int), B(float, float)> Make{};

使用方式:

A a = Make(3, 4); // (int, int)
B b = Make(3.14f, 6.28f); // (float, float)

Demo