简体   繁体   中英

Let template partial specializations share member functions with general instantiations

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM