Suppose I have a class template Foo
:
template<typename T, typename U>
struct Foo {
~Foo() = default;
// I want to reuse these methods for all instantiations of Foo
void bar() {}
void poi() {}
};
I want to specialize the destructor for any Foo<T, int>
, but I want Foo<T, int>
to share the other member functions with the general instantiations. But if I try to do:
template<typename T>
Foo<T, int>::~Foo()
{}
outside the class, it doesn't compile, the error being "invalid use of incomplete type struct Foo<T, int>
". What does this error mean, and how can I achieve what I'm trying to do?
It is not possible to partially specialise a (non-templated) member function. You need to specialise the whole class.
One way to do what you want is to inherit the common functions.
template<typename T, typename U> struct MemberProvider
{
~MemberProvider() = default;
void bar() {};
void poi() {};
};
template<typename T, typename U>
struct Foo : public MemberProvider<T, U>
{
~Foo() = default;
// Optionally, you can still do this.
// However, note that these HIDE the inherited functions
void bar() {};
void poi() {};
};
template<typename T> struct Foo<T, int> : public MemberProvider<T, int>
{
~Foo() {};
};
Note you will also need to include any members acted on by the common functions in the inherited class.
However, I would suggest the above represents a code smell. If I saw code like the above in a production environment, I'd be contemplating the presence of a design flaw. Bear in mind that, when destructing such a class, the most derived destructor is invoked before base class destructors. MemberProvider
is also not a polymorphic base.
You can explicitly specialize member functions of class templates, but you cannot partially specialize them - which is what you're trying to do.
If you need to partially specialize the destructor in a way that keeps the generic version defaulted, you'll have to partially specialize the whole class template:
template <class T, class U>
struct FooCommon {
~FooCommon() = default;
void bar() {}
void poi() {}
};
template <class T, class U>
struct Foo : FooCommon<T, U> { };
template <class T>
struct Foo<T, int> : FooCommon<T, int> {
~Foo() {
// special logic here
}
};
If foo
and poi
do things that depend on the template parameters, then you could extract your specialised destructor behaviour into a separate class, eg,
#include <iostream>
template <typename T, typename U>
struct Foo;
template <typename T, typename U>
struct FooDestructorTraits
{
static void destroy(Foo<T, U> * ptr)
{
std::cout << "default" << std::endl;
}
};
template <typename U>
struct FooDestructorTraits<int, U>
{
static void destroy(Foo<int, U> * ptr)
{
std::cout << "special" << std::endl;
}
};
template <typename T, typename U>
struct Foo
{
~Foo() noexcept { FooDestructorTraits<T, U>::destroy(this); }
void bar() {}
void baz() {}
};
int main() {
Foo<void, void> x;
Foo<int, void> y;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.