如何将std :: function或lambda作为(可选)模板参数?

时间:2012-10-30 17:33:26

标签: c++ c++11 template-meta-programming

您好我正在玩TMP并且正在考虑创建课程 看起来像是:

template<typename T, typename LogFunc>
class
{

(其中LogFunc应默认为“nop”函数)

想法是让一个类为T类实例定义一些功能,例如检查数字是否为偶数,还可以选择通过调用

来记录
void memberFunc(T& t)
{
  LogFunc(t); 
}

或者

void memberFunc(T& t)
{
  LogFunc lf;
  lf(t);
}

可以吗? 从阅读A on SO,lambdas有点像templ params一样有问题。 顺便说一句,如果有人关心this是我尝试的但是打印出来

  

:(

3 个答案:

答案 0 :(得分:3)

问题是lambda的类型是编译器强制的单例;它只有一个值,就是lambda本身;此外,该类型具有已删除的构造函数。因此,即使使用decltype,您也无法将lambdas作为模板实例化的一部分传递。但是没有什么可以阻止你将它们作为构造函数参数传递。

但是,这里我们遇到另一个问题:构造函数参数不用于推导模板实例化(这就是标准库提供make_pair和make_tuple等实用程序的原因)。所以我们需要一个模板化的工厂功能。

尽管如此,解决方案非常简单:

template<typename T, typename LogFunc>
class Foo {
  public:
    Foo(const T& t, LogFunc fn) : t_(t), lfn_(fn) {}
    //...

  private:
    T t_;
    LogFunc lfn_;
};

struct Noop {
  template<typename...A>
  void operator()(A...) { };
};

template<typename T, typename LogFunc=Noop>
Foo<T, LogFunc> make_foo(const T& t, LogFunc func=LogFunc()) {
  return Foo<T, LogFunc>(t, func);
}

答案 1 :(得分:2)

这不会直接回答,但会提供一些关于你做了什么的提示。

LogFunc参数是一种类型(不是对象),因此

  • LogFunc(t)创建一个临时LogFunc,将t作为参数(您实际上是在调用LogFunc::LogFunc(T&)构造函数)。
  • LogFunc lf; lf(t);创建一个堆栈生活默认构造Logfunc,名为lf,lf(t)调用其LogFunc::operator()(T&)成员函数。
  • LogFunc()(t)创建一个临时默认构造的LogFUnc,并在其上调用operator()(T&amp;)。

关于lambdas,它们实际上是构造函数获取捕获的变量的类,其operator()接受您声明的参数。但它们只存在于编译器的“内部”,并且没有可以引用的“名称”。

您可以做的是使用decltype或自由函数推断其类型。

通常,参数功能类存储在构造时初始化的frunction对象。

#include <iostream>

template<class Fn>
class LogFunc
{
public:
    LogFunc(Fn f) :fn(f) {}

    template<class T>
    void memberFunc(T& t)
    { fn(t); }
private:
    Fn fn;
};

template<class Fn>
LogFunc<Fn> makeLogFunc(Fn f)
{ return LogFunc<Fn>(f); }

int main()
{
    int x=5;

    auto lf = makeLogFunc([](int& a){ std::cout << a << std::endl; });
    lf.memberFunc(x);

    return 0;
}

编译为“g ++ -pedantic -Wall -std = c ++ 11”,并将ouptut

5

答案 2 :(得分:1)

其他答案都很好,但您也可以使用std::function<T>传递构造函数参数。看起来像这样:

#include <functional>
#include <iostream>

template <typename T> void someOther(T val){
  std::cout << "used other "<<val<<std::endl;
}

template <typename T> void noop(T val){
  std::cout << "noop "<<val<<std::endl;
}

template<typename T>
struct A{
  A(std::function<void(T)> f =noop<T> ) : mf(f){}
  void memberFunc(T valx){
    mf(valx);
  }
  std::function<void(T)> mf;
};

int main(){
  A<int> aNoop; ;
  A<float> aSomeOther{someOther<float>} ;
  aNoop.memberFunc(5);
  aSomeOther.memberFunc(3.55);
}

另一种方法是使用仿函数类,如下所示:

#include <iostream>
template <typename T> struct OtherC{
  void operator()(T v){ std::cout <<"other "<<v<<std::endl; };
};
template <typename T> struct NoopC{
  void operator()(T){ std::cout << "noop"<<std::endl; };
};
template<typename T, template <typename X> class F = NoopC >
struct A{
  static void memberFunc(T valx){ F<T>()(valx); }
};
int main(){
  A<int> aNoop; 
  A<float,OtherC> aSomeOther ;
  aNoop.memberFunc(5);
  aSomeOther.memberFunc(3.55);
}