我有这样一个类(.h文件):
class Entity {
public:
template<typename T, typename... Args>
T& addComponent(Args &&... args) {
// add component and return
}
};
当我得到一个特定的类型,我想要一个不同的行为,所以我试图重载"addComponent"像这样的方法(.cpp文件):
template<>
TransformComponent& Entity::addComponent<TransformComponent>() {
printf("overloaded! n");
// add component and return
}
方法调用是这样进行的:
entity.addComponent<TransformComponent>();
但是方法重载永远不会被调用。
编辑1:
我已经把方法声明移到了头文件中,
class Entity {
public:
template<typename T, typename... Args>
T& addComponent(Args &&... args) {
// add component and return
}
template<>
TransformComponent& addComponent<TransformComponent>() {
// add component and return
}
};
但现在我有另一个问题,一个编译错误(显式专门化在非命名空间范围)。显然有解决这个问题的办法,但不是很好的办法(链接在评论中)。
编辑2:
@aschepler的解决方案为我工作(在GCC和clang上工作)。由于某些原因,我在这篇文章中的第一个编辑是在clang上工作/编译的,我不知道为什么。
原始代码的问题在于,当代码想要使用该模板函数的专门化时,它必须知道显式专门化存在。如果显式特化只在一个翻译单元中找到(通常意味着一个*.cpp文件加上包含的头文件),那么其他翻译单元使用该特化是无效的,因为编译器通常不知道此时它不应该只使用一般定义。
所以头文件必须有一个显式专门化的声明,可能是也可能不是定义。
根据c++ 14和更早的版本,显式特化声明只能出现在命名空间作用域中,永远不能出现在类定义中,除非作为friend
。在c++ 17及以后的版本中,显式特化可以在其主模板所能声明的任何地方声明,因此第一次编辑后的代码是有效的。
clang++编译器允许在类范围内声明和/或定义专门化,无论-std
选项是什么。(这是安全的,因为它在旧版本中不可能意味着不同的东西。)g++编译器还没有实现新的更宽松的规则;这是GCC bug 85282。
因此,如果代码必须与g++一起工作,则需要在同一个头文件中在类定义之外进行声明。最佳实践是尽可能快地声明任何专门化,以尽量减少任何代码在声明专门化之前试图使用它的可能性。有时这意味着在主模板声明和/或定义之后不久,有时意味着在定义用作模板参数的某种类型之后不久。这里指的是在包含模板的类定义之后不久。
class Entity {
public:
template<typename T, typename... Args>
T& addComponent(Args &&... args) {
// add component and return
}
};
template<>
inline TransformComponent& Entity::addComponent<TransformComponent>() {
// add component and return
}
或者它实际上不需要内联和在标题中。由于没有模板参数,您可以在头文件中声明它,并在源文件中定义它:
// HEADER
class Entity {
public:
template<typename T, typename... Args>
T& addComponent(Args &&... args) {
// add component and return
}
};
template<>
TransformComponent& Entity::addComponent<TransformComponent>();
// SOURCE
#include "Entity.hpp"
template<>
TransformComponent& Entity::addComponent<TransformComponent>() {
// add component and return
}
(你在评论中链接的问题是关于一个函数模板,它是类模板的成员,当一个人想要专门化函数模板,但作为一般类模板的成员。这不能直接做到。因为你的函数模板是一个非模板类的成员,事情就简单多了。)