任意类的const和非const成员函数的模板包装器

时间:2014-07-22 13:23:03

标签: c++ templates const member-function-pointers

我想要一个模板化的类(包装器),它可以使用所有可能的类(T)并使用这些类(函数)的成员函数来执行(这里评估)。

我发现了类似的请求,您可以看到herehere,但两者都无法满足以下两个条件。

条件:

  1. 两者都必须可以在包装类中访问指向类实例(T * ptr)的指针和指向成员函数(函数)的指针。

  2. 包装类应该同时使用const和非const成员函数。

  3. 这里的代码仅适用于非const:

    #include <iostream>
    #include <math.h>
    
    template< class T, double (T::*fck) (double) >
    struct Wrapper
    {
      Wrapper( T * ptrT);
    
      double evaluate( double );
    
    protected:
    
      T * myPtrT;
    };
    
    
    template< class T, double (T::*fck) (double) >
    Wrapper<T, fck>::Wrapper( T * ptrT) : myPtrT(ptrT) {}
    
    
    template< class T, double (T::*fck) (double) >
    double Wrapper<T, fck>::evaluate( double s )
    { return (myPtrT->*fck)(s); }
    
    
    struct kernel
    {
      double gauss( double s )
      {
        return exp(-0.5*s*s);
      }
    };
    
    int main()
    {
      kernel G;
    
      Wrapper<kernel, &kernel::gauss> myKernel ( &G );
    
      std::cout<< myKernel.evaluate(0.0) <<std::endl;
      std::cout<< myKernel.evaluate(0.3) <<std::endl;
    
      return 0;
    }
    

2 个答案:

答案 0 :(得分:3)

Wrapper类是否严格必要?您似乎正在尝试为提供标准签名的函数的类创建通用评估机制:double f(double)。使用std::function(C ++ 11)或boost::function(C ++ 03)可以很容易地解决这个问题。

在C ++ 03中使用boost::functionboost::bind

#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <math.h>

struct kernel
{
  double gauss( double s )
  {
    return exp(-0.5*s*s);
  }
};

int main()
{
    kernel G;

    typedef boost::function<double (double)> MyWrapper;

    MyWrapper w( boost::bind(&kernel::gauss, G, _1) );
    std::cout << w(0.0) << std::endl;
    std::cout << w(0.3) << std::endl;

    return 0;
}

答案 1 :(得分:2)

所有编程问题都可以通过另一个抽象层次来解决,除了太多的抽象层次。

template< class T, class Op >
struct Wrapper
{
  Wrapper():t(nullptr){}
  Wrapper( T* ptrT ):t(ptrT){}
  Wrapper( Wrapper const& ) = default;
  Wrapper& operator=( Wrapper const& ) = default;

  template<class...Args>
  auto operator()(Args&&...args) const->decltype( Op{}(std::declval<T*>(),std::declval<Args>()...)) {
    return Op{}(t, std::forward<Args>(args)...);
  }
  T* t;
};

template< class T, bool isConst, class Sig >
struct MemberFunc;
template< class T, class R, class... Args >
struct MemberFunc<T, false, R(Args...) > {
  template< R(T::*func)(Args...) >
  struct type {
    template<class... Ts>
    R operator()(T* t, Ts&&...ts) const {
      return (t->*func)(std::forward<Ts>(ts)...);
    }
  };
};
template< class T, class R, class... Args >
struct MemberFunc<T, true, R(Args...) > {
  template< R(T::*func)(Args...) const >
  struct type {
    template<class... Ts>
    R operator()(T const* t, Ts&&...ts) const {
      return (t->*func)(std::forward<Ts>(ts)...);
    }
  };
};

struct kernel
{
  double gauss( double s )
  {
    return exp(-0.5*s*s);
  }
};

int main()
{
  kernel G;

  Wrapper<kernel, MemberFunc<kernel, false, double(double)>::type<&kernel::gauss>> myKernel(&G);

  std::cout<< myKernel(0.0) <<std::endl;
  std::cout<< myKernel(0.3) <<std::endl;

  return 0;
}

live example

MemberFunc::type基本上是一个编译时评估std::mem_fun

Wrapper现在将无状态仿函数作为其第二个参数,它完美地转发。 MemberFunc<...>::type<...>构建了一个包含成员函数的无状态函子。

我冒昧地使它与任意函数签名一起工作,并且摆脱.evaluate - 我们有一个invokation操作符,如果我们有一个要调用它的工作的实例,只需调用它。

当然,这也可以用lambda:

完成
auto myKernel = [G](double s)->double { return G->gauss( s ); };

myKernel的类型阻止它在某些情况下很容易存储和返回,而不会对其进行类型删除。类型擦除它会增加运行时间接,这会产生运行时成本。

您应该首先尝试使用类型擦除的std::function解决方案,看看它是否有性能成本,因为代码更简单,更容易阅读。