如何在 c++ 中推断列表末尾的模板参数

How can I deduce template parameters at the end of the list in c++?

本文关键字:参数 列表 c++      更新时间:2023-10-16

我正在尝试编写一个基于三件事模板化的函数:

  1. 第一种类型。
  2. 第二种类型。
  3. 带有参数的函数 第一和第二类型。

代码如下所示:

#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).

编辑2:我的意思是我想写foo<&bar>(1,2)

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

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

甚至可以避免使用包含该值的临时struct键入bar两次。结构将被任何像样的编译器优化出来,并且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