模板是一种先进的技术静态多态性 http://www.cpptutor.com/static-polymorphism-and-dynamic-polymorphism-in-c++.htm。在类型化语言(如 C++)中,如果没有静态多态性,您将必须单独定义使用的每个实体并精确指示引用的每个实体。
C++ 中的静态多态性机制允许自动指示函数或方法,并将其推迟到通过构建超载。它允许您通过以下方式同时定义共享某些特征的多个实体模板并将特定专业的定义推迟到构建(从使用中推断)。
(请注意,在各种场景中,静态多态性允许单独的代码,因此对使用和定义的更改是独立的,这非常有用。)
此机制的重要含义是模板的每个专业化都可能具有不同的类型。截至我回复时,尚不清楚您是否想要在一种类型的容器中存储指向单一或多种类型专门化的指针。可能性还取决于函数模板的参数和结果类型。
C++ 中的函数的类型是其参数类型列表和返回类型的组合。换句话说,接受和返回相同类型的两个函数属于相同类型。如果您的函数模板既没有接受也没有返回模板参数类型(即T
)也不是模板类型(例如std::vector<T>
),此函数模板的每个特化都将采用并返回相同的类型,因此将是相同类型的函数。
template <typename T>
int func() { ... }
这个(可以说是无用的)函数模板不带参数并返回int
, 任何T
用于专门化模板。因此,只要参数定义为,就可以使用指向它的指针int (*f)()
。在这种情况下,您可以在一个向量中保留指向任何专业化的指针。
typedef std::vector<std::string> string_vt;
typedef int func_t();
typedef func_t* funcPointer;
typedef std::vector<funcPointer> funcPointer_vt;
funcPointer x = &func<int>;
funcPointer y = &func<float>;
可以看出,函数模板的每个特化都具有相同的类型,并且两个指针都适合在同一个容器中。
下一个案例 - 如果函数头依赖于模板参数怎么办?每个专业化都会有不同的签名,即不同的函数类型。指向所有这些的指针将具有不同的类型 - 所以甚至不可能typedef
这个指针一次。
template <typename T>
void func(std::vector<T> param) { ... }
在这种情况下,函数模板专业化具有不同类型,具体取决于T
用于专门化。
typedef int func_t_int(std::vector<int>);
typedef func_t_int* funcPointerInt;
typedef std::vector<funcPointerInt> funcPointerInt_vt;
typedef float func_t_float(std::vector<float>);
typedef func_t_float* funcPointerFloat;
typedef std::vector<funcPointerFloat> funcPointerFloat_vt;
funcPointerInt x = &func<int>;
funcPointerFloat x = &func<float>;
专业化有不同的类型,因为它们采用不同类型的向量。指针不适合放在同一个容器中。
此时值得一提的是,在这种情况下,没有必要单独定义每个指针类型。它们可以是模板类型:
template <typename T>
using funcPointer = void (*)(std::vector<T>);
现在允许funcPointer<int>
用作类型限定符,代替之前的funcPointerInt
.
funcPointer<float> y = &func<float>;
在更复杂的情况下,可以创建一个模板,其每个特化都是不同的类型,然后使用具体向量的单个实例来存储指向仅模板的特化之一类型的函数的各种指针。尽管像示例中这样的简单模板只能为每种类型生成一个函数,但因为每个特化都会生成一种类型的函数和该类型的一个函数,但并非不可能设想一种场景,其中获得了指向函数的各种指针,两者都指向特化和通常的功能,也许来自不同的来源。所以这项技术可能有用。
但另一种情况是,尽管模板的每个专业化都具有不同的类型,但仍然需要将指向各种专业化的指针存储在单个模板中。std::vector
。在这种情况下动态多态性会有帮助的。为了存储不同类型的值,fe。在一种类型的变量中,指向不同类型函数的指针需要遗产。可以将任何子类存储在定义为超类的字段中。Note然而,这不太可能真正完成任何事情,而且可能不是您真正想要的。
我现在看到两种普遍的可能性。要么使用带有方法的类模板,该方法继承自非模板类。
template <typename T>
class MyClass : BaseClass
{
public:
T operator()(const T& param, int value);
}
MyClass<int> a;
MyClass<float> b;
BaseClass* ptr = &a;
ptr = &b;
虽然此类的每个专业化可能属于不同类型,但它们都共享超类BaseClass
,所以一个指向 a 的指针BaseClass
实际上可以指向它们中的任何一个,并且std::vector<funcPointerBase>
可以用来存储它们。通过超载operator()
我们创建了一个模仿函数的对象。这种类的有趣属性是它可以具有使用参数构造函数创建的多个实例。因此,类模板有效地生成多种类型的特化,而每个特化类又可以生成不同参数化的实例。
template <typename T>
class MyClass : BaseClass
{
int functor_param;
public:
MyClass(int functor_param);
T operator()(const T& param, int value);
}
此版本允许创建以不同方式工作的实例:
MyClass<int> a(1);
MyClass<int> b(2);
MyClass<float> c(4);
MyClass<int>* ptr = &a;
ptr = &b;
ptr = &c;
我不是函子方面的专家,只是想介绍一下总体思路。如果它看起来很有趣,我建议现在就研究一下。
但从技术上讲,我们不存储函数指针,只存储常规对象指针。嗯,如前所述,我们需要继承来使用一种类型的变量来存储各种类型的值。因此,如果我们不使用继承来将过程函数交换为动态多态的东西,那么我们必须对指针做同样的事情。
template <typename T>
T func(std::pair < T, char>) {}
template <typename T>
using funcPointer = T(*)(std::pair<T, char>);
template <typename T>
class MyPointer : BasePointer
{
funcPointer<T> ptr;
public:
MyPointer(funcPointer<T> ptr);
T()(std::pair <T, char>) operator*(std::pair <T, char> pair)
{
*ptr(pair);
}
};
这再次允许创建单个std::vector<BasePointer>
存储所有可能的伪函数指针。
现在是非常重要的一点。在这两种情况下,你会如何称呼它们?因为在这两种情况下它们都存储在单个std::vector<>
,它们被视为基本类型。特定的函数调用需要特定类型的参数并返回特定类型。如果所有子类都可以以相同的方式执行任何操作,则可以通过在基类中定义这样的方法来公开它(在任一情况下使用函子或指针..or?),但特定的专用函数调用不是那种类型的东西。经过所有这些斗争之后,您最终想要执行的每个函数调用都将是不同的类型,需要不同类型的参数和/或返回不同类型的值。因此,它们永远不可能都适合通常的同一个地方,而不是模板化的代码,在相同的执行环境下。如果他们这样做了,那么首先就不需要动态多态性来解决这个问题。
可以做的一件事 - 这是非常不鼓励的,并且可能违背动态多态性的目的 - 是在运行时检测子类类型并相应地进行。如果您确信自己有一个很好的使用此功能的案例,请进行研究。但最有可能的是,这可能是一个很大的反模式。
但从技术上讲,你想做的任何事情都是可能的。