c++ - 未调用模板方法重载
问题描述
我有这个类(.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 错误 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
}
(您在评论中链接的问答是关于作为类模板成员的函数模板,当一个人想要专门化函数模板但作为通用类模板的成员时。这不能直接完成。因为你的函数模板是非模板类的成员,事情更简单。)
推荐阅读
- port - Service Fabric 端口问题来宾可执行 Web api
- php - MySQL + PHP:如何处理存储过程参数中的变音符号?
- typescript - 带条件类型的简单函数
- node.js - 客户端加密库
- actions-on-google - Google Action 未获批准
- javascript - DOM 和 Javascript 如何交互?
- python-3.x - 输出文件为空,在 docker 容器的 ubuntu16.04 中没有进行任何编码(检查 -ss / -t / -frames 参数,如果使用)
- sql-server - 用户“NT AUTHORITY\SYSTEM”登录失败
- mongodb - 如何过滤猫鼬中的子文档和人口
- c++ - Qt + QML 应用程序 Windows 部署不起作用