简体   繁体   中英

Function Template Specialization Syntax aggregating templated types

With respect to function template specialization, I would like help with a bit of syntax. The following is a simplified scenario:

Base header:

template <typename T>
void Foo(T* t) { TRACE("Default Foo impl"); }  // <-- default implementation

template <typename T>
struct Base
{
    explicit Base()
    {
        static_assert(std::is_base_of<Base, T>::value, "T must derive from Base");
        Foo(static_cast<T*>(this));
    }
};

Derived_X header:

struct Derived_X : public Base<Derived_X>
{
    explicit Derived_X() : Base<Derived_X> { } { }
};

// no specialization will be implemented --> using default

Derived_Y header:

struct Derived_Y : public Base<Derived_Y>
{
    explicit Derived_Y() : Base<Derived_Y> { } { }
};

template <>  // Foo specialization for Derived_Y
void Foo<Derived_Y>(Derived_Y* t)
{
    Foo(static_cast<Base<Derived_Y>*>(t));  // <-- call default impl
    TRACE("Derived_Y Foo impl");
}

Derived_Z header:

template <typename T>
struct Derived_Z : public Base<T>
{
    explicit Derived_Z() : Base<T> { }
    {
        static_assert(std::is_base_of<Derived_Z, T>::value, "T must derive from Derived_Z");
    }
};

/*  What does this specialization look like?
template <typename T>
void Foo<Derived_Z<T>>(Derived_Z<T>* t)
{
    Foo(static_cast<Base<T>*>(t));  // <-- call default impl
    TRACE("Derived_Z<T> Foo impl");
}
// */

MostDerived Header:

struct MostDerived : public Derived_Z<MostDerived>
{
    explicit MostDerived() : Derived_Z<MostDerived> { } { }
};

template <>
void Foo<MostDerived>(MostDerived* t)
{
    Foo(static_cast<Derived_Z<MostDerived>*>(t));  // <-- call Derived_Z impl
    TRACE("MostDerived Foo impl");
}

Usage:

int main()
{
    Derived_X dx { };   // <-- "Default Foo impl"
    Derived_Y dy { };   // <-- "Default Foo impl" && "Derived_Y Foo impl"
    MostDerived md { }; // <-- "Default Foo impl" && "MostDerived Foo impl"
}

I have not been able to determine how to specialize Foo for Derived_Z . Any help would be most appreciated!

It's generally considered poor form to specialize function templates. You won't be able to partially specialize (as you have noticed), and they won't be considered in overload resolution. Rather, just create overloads, and let overload resolution take care of the rest.

// no template at all for Derived_Y
void Foo(Derived_Y* t)
{
    Foo(static_cast<Base<Derived_Y>*>(t));  // <-- call default impl
    TRACE("Derived_Y Foo impl");
}

// a regular template (no specialization) for Derived_Z<T>
template <typename T>
void Foo(Derived_Z<T>* t)
{
    Foo(static_cast<Base<T>*>(t));  // <-- call default impl
    TRACE("Derived_Z<T> Foo impl");
}

// again, no template for MostDerived
void Foo(MostDerived* t)
{
    Foo(static_cast<Derived_Z<MostDerived>*>(t));  // <-- call Derived_Z impl
    TRACE("MostDerived Foo impl");
}

Now, you might want to consider changing the base implementation to only accept a Base<T>* rather than a T* . Say you have Derived_Y2 that derives from Derived_Y , but you have not defined an overload for Foo(Derived_Y2*) . Calling Foo() using a pointer to a Derived_Y2 will then go to Foo(T*) with T being deduced as a Derived_Y2 , as that is a better match than Foo(Derived_Y*)

struct Derived_Y2 : Derived_T { };
Derived_Y2 y2; // "Default Foo impl"

By changing the base implementation to:

template<class T>
void Foo(Base<T>*) { TRACE("Default Foo impl"); }

Foo(Derived_Y*) will now be a better match when given a pointer to a Derived_Y2 , because it is more specialized as they say.

I have not been able to determine how to specialize Foo for Derived_Z.

It's because you can't partial specialize a template function.

But you can partial specialize a class / struct .

So I propose the use of an helper struct

template <typename T>
struct bar
 {
   static void func (T *)
    { std::cout << "default bar" << std::endl; }
 };

template <>
struct bar<Derived_Y>
 {
   static void func (Derived_Y *)
    { std::cout << "Derived_Y bar" << std::endl; }
 };

template <typename T>
struct bar<Derived_Z<T>>
 {
   static void func (Derived_Z<T> *)
    { std::cout << "Derived_Z<T> bar" << std::endl; }
 };

and Foo() simply become

template <typename T>
void Foo (T * t)
 { bar<T>::func(t); }

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