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

Partial function/method template specialization workarounds

本文关键字:方法 专用 解决 函数      更新时间:2023-10-16

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

//ILLEGAL
template<typename T>
void Foo<T,2>::do_something()
{
  arr_[0]->greet();
}

下面是完整的代码和我当前的(丑陋的(解决方案。 我必须专攻do_something()两次,一次用于Base,一次用于Derived. 如果存在多个方法(例如 do_something()(可以在特殊 N = 2 的情况下进行优化,并且如果存在许多 Base 子类,这会变得丑陋。

#include <iostream>
#include <memory>
class Base
{
public:
  virtual void speak()
  {
    std::cout << "base is speaking" << std::endl;  
  }
  virtual void greet()
  {
    std::cout << "base is greeting" << std::endl;  
  }
};
class Derived : public Base
{
public:
  void speak()
  {
    std::cout << "derived is speaking" << std::endl;  
  }
  void greet()
  {
    std::cout << "derived is greeting" << std::endl;  
  }
};
template<typename T, int N>
class Foo
{
public:
  Foo(std::array<std::unique_ptr<T>, N>&& arr) :
    arr_(std::move(arr))
  {
  }
  void do_something();
  std::array<std::unique_ptr<T>, N> arr_;
};
template<typename T, int N>
void Foo<T,N>::do_something()
{
  arr_[0]->speak();
}
//Want to avoid "copy-and_paste" of do_something() below
template<>
void Foo<Base,2>::do_something()
{
  arr_[0]->greet();
}
template<>
void Foo<Derived,2>::do_something()
{
  arr_[0]->greet();
}
int main()
{
  constexpr int N = 2;
  std::array<std::unique_ptr<Derived>, N> arr = 
    {
      std::unique_ptr<Derived>(new Derived),
      std::unique_ptr<Derived>(new Derived)
    };
  Foo<Derived, N> foo(std::move(arr));
  foo.do_something();
  return 0;
}

诀窍是将实现转发到帮助程序模板类,并部分专用化该类和/或使用标记调度:

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

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

第一个是将请求转发到模板类中的静态函数,这允许部分专用化:

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(基包含差异、派生类型泛型代码并从基中提取特定实现(。