如何在c ++的列表末尾推导出模板参数?

时间:2015-01-29 11:56:54

标签: c++ templates template-templates

我正在尝试编写一个模仿三件事的函数:

  1. 第一种类型。
  2. 第二种类型。
  3. 使用参数First和Second类型的函数。
  4. 代码如下所示:

    #include <iostream>
    #include <typeinfo>
    using namespace std;
    
    // Assume that this function is in a library.  Can't be modified.
    void bar(int x, int y) {
      cout << x << endl;
      cout << y << endl;
    }
    
    // My code is below:
    template <typename Type1, typename Type2, void (*fn)(Type1, Type2)>
    void foo(Type1 x1, Type2 x2) {
      fn(x1,x2);
    }
    
    int main() {
      foo<int, int, &bar>(1,2);
    }
    

    代码有效,但我不满意我的模板必须包含<int, int, &bar>。我希望编译器会发现bar有int, int作为参数并计算出来。

    我尝试首先列出函数,然后在类型声明中使用,但在声明中,Type1在函数原型中未被识别,因为它稍后在同一原型中定义。

    有优雅的解决方案吗?

    编辑:我绝对不想在堆栈上传递指向bar的指针。我想在bar上模仿。参数应该只是(1, 2)

    Edit2:就此而言,我的意思是我想写foo<&bar>(1,2)

2 个答案:

答案 0 :(得分:3)

这是我没有将函数作为参数传递的解决方案:

void bar(int a, int b) {
  cout <<  a << " " << b << endl;
}

template <class F, F *fn>
struct Foo {

  template <class... Args>
  static decltype(auto) foo(Args &&... args) {
    return fn(std::forward<Args>(args)...);
  }
};

int main() {

  Foo<decltype(bar), bar>::foo(1, 2);

  return 0;
}

正如你所看到的那样,你必须两次写bar,一次写它的类型,一次它的价值,但我认为这是一个小小的不便。

或简单版本(如果你不能使用c ++ 11)

template <class F, F* fn>
struct Foo {

  template <class T1, class T2>
  static void foo(T1 t1, T2 t2) {
    fn(t1, t2);
  }
};

对于那些不介意将函数obj作为参数传递的人:

选项1:

template <class T1, class T2>
void foo(T1 x1, T2 x2, void (*fn)(T1, T2)) {
  fn(x1, x2);
}

foo(1, 2, bar);

选项2:

template <class T1, class T2, class F = void(*)(T1, T2)>
void foo(T1 x1, T2 x2, F fn)) {
  fn(x1, x2);
}

foo(1, 2, bar);

选项3:

template <class T1, class T2, class F>
void foo(T1 x1, T2 x2, F fn)) {
  fn(x1, x2);
}

foo(1, 2, bar);

选项3b(真实交易):

template <class T1, class T2, class F>
void foo(T1 &&x1, T2 &&x2, F &&fn)) {
  std::forward<F>(fn)(std::forward(x1), std::forward(x2));
}

foo(1, 2, bar);

选项4(真实的交易)(好......取决于你需要的东西)

template <class F, class... Args>
decltype(auto) foo(F &&fn, Args &&... args) {
  return std::forward<F>(fn)(std::forward<Args>(args)...);
}

答案 1 :(得分:0)

您甚至可以避免使用包含该值的临时bar键入struct两次。该结构将由任何体面的编译器优化,bar将不会在堆栈上传递,尽管作为参数传递给foo

template <typename Fn>
struct FooT {
  Fn &fn;
  FooT(Fn fn):fn(fn){}
  operator Fn *() {return fn;}
};

template <typename Fn>
inline FooT<Fn> foo(Fn *fn) {return FooT<Fn>(fn);}


int main() {
  foo(bar)(1,2);
}

注意:转换为Fn *可以直接调用fn,无需任何参数转发。如果需要,也可以使用模板operator ()传递/转发参数来调用它,但这似乎更简单且有效。如果您需要在调用fn之前或之后执行更多操作,模板化实现可能会很方便。我不是前瞻性的论点,只是传递它们,为了保持简单,如果有必要,这可以很容易地改变:

template <typename Fn>
struct FooT {
  Fn &fn;
  FooT(Fn fn):fn(fn){}
  template <typename Type1, typename Type2>
  void operator ()(Type1 x1, Type2 x2) {
    fn(x1,x2);
  }
};

Visual Studio 2012生成的代码,禁用整个程序优化,仅扩展内联(/ Ob1):

  foo(bar)(1,2);
011E16F0  push        2  
011E16F2  push        1  
011E16F4  call        bar (011E13D0h)  
011E16F9  add         esp,8