Специализация шаблона функции Синтаксис агрегирования шаблонных типов

Что касается специализации шаблонов функций, я хотел бы немного помочь с синтаксисом. Ниже приведен упрощенный сценарий:

Базовый заголовок:

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:

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

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

Derived_Y заголовок:

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:

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");
}

Использование:

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"
}

Я не смог определить, как специализироваться Foo за Derived_Z, Любая помощь будет наиболее ценится!

2 ответа

Решение

Обычно считается плохой формой специализировать шаблоны функций. Вы не сможете частично специализироваться (как вы заметили), и они не будут учитываться при разрешении перегрузки. Скорее, просто создайте перегрузки и позвольте разрешению перегрузки позаботиться обо всем остальном.

// 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");
}

Теперь вы можете рассмотреть возможность изменения базовой реализации, чтобы принять только Base<T>* а не T*, Скажи у тебя Derived_Y2 что происходит от Derived_Y, но вы не определили перегрузку для Foo(Derived_Y2*), призвание Foo() используя указатель на Derived_Y2 затем пойдет в Foo(T*) с T выводится как Derived_Y2, так как это лучше, чем Foo(Derived_Y*)

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

Изменяя базовую реализацию на:

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

Foo(Derived_Y*) теперь будет лучше соответствовать, когда указатель на Derived_Y2потому что он более специализирован, как говорится.

Я не смог определить, как специализировать Foo для Derived_Z.

Это потому, что вы не можете частично специализировать шаблонную функцию.

Но вы можете частично специализировать class/struct,

Поэтому я предлагаю использовать помощника 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; }
 };

а также Foo() просто стать

template <typename T>
void Foo (T * t)
 { bar<T>::func(t); }
Другие вопросы по тегам