部分函数/方法模板特化解决方法

时间:2014-12-20 02:03:47

标签: c++ templates inheritance template-specialization partial-specialization

我知道函数和类方法不支持部分模板特化,所以我的问题是:解决此问题的常见解决方案或模式是什么? Derived下面的Base派生自{{ 1}},这两个类都有虚方法greet()speak()Foo包含std::array<unique_ptr<T>, N>,并在do_something()中使用。 Foo有两个模板参数:T(类类型)和Nstd::array的元素数量)如果N = 2,则存在高度优化的do_something()版本。现在假设Foo的{​​{1}}参数并不总是基类T。理想情况下,我想编写以下代码,但这是非法的:

Base

以下是完整代码和我当前(丑陋)的解决方案。我必须专门//ILLEGAL template<typename T> void Foo<T,2>::do_something() { arr_[0]->greet(); } 两次,一次为do_something(),一次为Base。如果存在多个方法(例如Derived)可以在特殊do_something() = 2情况下进行优化,并且存在许多N的子类,则会变得很难看。

Base

2 个答案:

答案 0 :(得分:3)

诀窍是将实现转发给帮助器模板类,并部分专门化该类和/或使用标签分派:

namespace {
    template<typename T, int N, bool isBase = std::is_base_of<Base, T>::value>
    struct helper {
        // general case: 
        void operator () (std::array<std::unique_ptr<T>, N>& arr_) const
        {
            arr_[0]->speak();
        }
    };

    template<typename T>
    struct helper<T, 2, true>
    {
        void operator () (std::array<std::unique_ptr<T>, 2>& arr_) const
        {
          arr_[0]->greet();
        }
    };

    // You may add other specialization if required.

}

template<typename T, int N>
void Foo<T,N>::do_something()
{
    helper<T, N>()(arr_);
}

答案 1 :(得分:0)

有不同的选择,取决于问题中的其他约束可能比另一个更合适。

第一个是将请求转发给模板类中的静态函数,该函数允许部分特化:

template <int N>
struct Helper {
   template <typename T>
   static void talk(T& t) {  // Should be T const &, but that requires const members
       t.speak();
   }
};
template <>
struct Helper<2> {
   template <typename T>
   static void talk(T& t) {
       t.greet();
   }
}

然后do_something的实现将是:

template <typename T, int N>
void Foo<T,N>::do_something() {
   Helper<N>::talk(*arr_[0]);
}

或者,您可以使用标记分派来选择多个重载之一:

template <int N> struct tag {};

template <typename T, int N>
template <int M>
void Foo<T,N>::do_something_impl(tag<M>) {
    arr_[0]->speak();
}

template <typename T, int N>
void Foo<T,N>::do_something_impl(tag<2>) {
    arr_[0]->greet();
}

template <typename T, int N>
void Foo<T,N>::do_something() {
    do_something_impl(tag<N>());
}

我创建了一个可以专门用于任何可能N的标记类型。您还可以使用C ++ 11中的现有工具。

最后,如果你需要为不同的函数做这样的事情,你可以使用继承,并将一些功能推送到解决差异的基础。这可以通过将公共代码推送到基础,差异到中间级别并使用仅从其余部分继承的较低级别类型来完成(基础包含通用代码,派生类型专门化)。或者使用CRTP(base(s)包含差异,派生类型通用代码并从基础中提取特定实现。