Reputation: 53
I have this class (.h file):
class Entity {
public:
template<typename T, typename... Args>
T& addComponent(Args &&... args) {
// add component and return
}
};
When I get an specific type, i want a different behavior, so I'm triying to overload the "addComponent" method like this (.cpp file):
template<>
TransformComponent& Entity::addComponent<TransformComponent>() {
printf("overloaded! \n");
// add component and return
}
the method call is made like this:
entity.addComponent<TransformComponent>();
but the method overload is never called.
edit 1:
I have moved the method declaration to the header file, has sugested.
class Entity {
public:
template<typename T, typename... Args>
T& addComponent(Args &&... args) {
// add component and return
}
template<>
TransformComponent& addComponent<TransformComponent>() {
// add component and return
}
};
But now i have another problem, a compile error (explicit specialization in non-namespace scope). Apparently there are solutions for this problem, but not very good ones (link in the comments).
edit 2:
@aschepler 's solution worked for me (working on gcc and clang). For some reason my first edit on this post is working/compiling on clang, and I'm not shure why.
Upvotes: 0
Views: 265
Reputation: 72271
The trouble with the original code is that when code wants to use a specialization of that template function, it must know that the explicit specialization exists. If the explicit specialization is only found in one translation unit (generally meaning a single *.cpp file plus included headers), then it's invalid for a different translation unit to use that specialization, since the compiler doesn't ordinarily know at that point that it shouldn't just use the general definition.
So the header has to have a declaration of the explicit specialization, which might or might not be a definition.
According to C++14 and earlier, an explicit specialization declaration can only appear at namespace scope, never inside a class definition except as a friend
. In C++17 and later, an explicit specialization can be declared anywhere its primary template can, so the code after your first edit is valid.
The clang++ compiler allows the specialization to be declared and/or defined in class scope no matter what -std
option is in effect. (This is safe since it couldn't possibly mean something different in older versions.) The g++ compiler has not yet implemented the newer more permissive rule at all; this is GCC bug 85282.
So if the code must work with g++, you need a declaration in the same header file, outside the class definition. Best practice is to declare any specializations nearly as soon as possible, to minimize chances any code would attempt to use the specialization before it's declared. Sometimes that means soon after the primary template is declared and/or defined, and sometimes that means soon after some type used as a template argument is defined. Here it means soon after the class containing the template is defined.
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
}
Or it doesn't actually need to be inline and in the header. Since there are no template parameters, you can declare it in the header and define it in a source file:
// 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
}
(The Q&A you linked in the comments is about a function template which is a member of a class template, when one wants to specialize the function template but as a member of the general class template. That can't be done directly. Since your function template is a member of a non-template class, things are simpler.)
Upvotes: 3