首页 > 解决方案 > 未调用模板方法重载

问题描述

我有这个类(.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 上工作/编译,我不知道为什么。

标签: c++

解决方案


原始代码的问题在于,当代码想要使用该模板函数的特化时,它必须知道存在显式特化。如果显式特化仅在一个翻译单元中找到(通常意味着单个 *.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
}

您在评论中链接的问答是关于作为类模板成员的函数模板,当一个人想要专门化函数模板但作为通用类模板的成员时。这不能直接完成。因为你的函数模板是非模板类的成员,事情更简单。)


推荐阅读