【问题标题】:Passing any function as template parameter将任何函数作为模板参数传递
【发布时间】:2014-08-02 19:58:34
【问题描述】:

我想将函数作为模板参数传递给函数。目前我能做的最好的事情是:

template< typename F, F f >
void pass()
{
    ...
}

...使用的:

pass< decltype(&func), &func >();

我真正想要的是:

pass< &func >();

有没有什么方法可以在没有宏的情况下实现这一点?基本上同时传递类型和值?编译器显然拥有所需的所有信息......

解决方案必须使用可变参数和返回类型。函数值在编译时使用,因此不能作为参数传递。

欢迎使用 C++11 解决方案。


编辑: 用例 - 我在编译时生成绑定,我需要为每个传递的函数创建一个 C++ 函数。这段代码的用例看起来(简化)或多或少像这样:

template < typename F, F f > 
int function_wrapper( lua_State* L ) 
{
    return dispatcher<typename return_type<F>::type>::call( L, 1, f );
}

void register_native_function( lua_Function f, const char* name )
{
    // binding call using pure C function f
}

template < typename F, F f >
void register_function( const char* name )
{
    register_native_function( function_wrapper< F, f >, name );
}

请注意,我需要创建一个编译时函数包装器,所以我需要在编译时传递函数值。有一些绑定解决方案允许在运行时进行绑定,但与手写绑定相比,它们总是需要样板代码。我的目标是在这里实现手写的性能。

【问题讨论】:

  • 您是否衡量过template&lt;class F&gt; pass (F fun) 是否会给您带来任何开销?根本的问题是你不能传递除整数值以外的任何东西作为非类型模板参数。
  • 传递函数不会(显然)产生任何开销,但是在绑定诸如 vec3d 类(其方法可能被调用数千次)之类的东西时,使用泛型调用函数的需求非常明显在一帧中。
  • @TemplateRex "你不能传递除整数值以外的任何东西" --- 这不完全正确。
  • 传递模板未知类型的非类型模板参数的唯一方法是将其类型作为类型模板参数传递,然后传递值。所以decltype(&amp;func), &amp;func 是你能得到的最好的。

标签: c++ templates c++11 function-pointers


【解决方案1】:

现在可以在 C++17 中使用 template&lt;auto&gt;

template<auto Func>
struct FuncWrapper final
{
    template<typename... Args>
    auto operator()(Args &&... args) const
    {
        return Func(std::forward<Args>(args)...);
    }
};

int add(int a, int b)
{
    return a + b;
}

int main()
{
    FuncWrapper<add> wrapper;
    return wrapper(12, 34);
}

演示:https://godbolt.org/g/B7W56t

您可以使用#ifdef __cpp_nontype_template_parameter_auto 在您的代码中检测编译器对此的支持。

如果你能够使用 C++20 并且想要更好的错误信息,你也可以使用概念:

template<typename T>
concept CanAddTwoNumbers = std::is_invocable_r_v<int, T, int, int>;

template<auto Func>
    requires CanAddTwoNumbers<decltype(Func)>
struct AddTwoNumbersWrapper final
{
    auto operator()(int a, int b) const
    -> int
    {
        return std::invoke(Func, a, b);
    }
};

int add(int a, int b)
{
    return a + b;
}

int main()
{
    AddTwoNumbersWrapper<add> wrapper;
    return wrapper(12, 34);
    AddTwoNumbersWrapper<123> bad; //error: constraint failure
}

演示:https://gcc.godbolt.org/z/ai3WGH

【讨论】:

  • 我想知道你是否可以为重载功能扩展你的解决方案。例如,如果您的 have 和 add 采用 int 和 add 采用 float,我相信 template&lt; auto Func&gt;can't resolve 重载集并且编译失败
  • @monamimani 您可以将重载函数名称强制转换为要使用的函数签名,就像在处理重载函数的任何其他情况下一样。
  • 是的,谢谢,但这会导致丑陋的调用者代码。我只是问一个问题,看看我是否可以改进它。 stackoverflow.com/questions/70911263
  • @monamimani 不幸的是,这似乎是 C++ 如何处理重载函数的固有问题。处理它们的唯一方法是调用它们或施放它们。你不能对整个重载集做任何特殊或自动的事情。充其量你可以使用转发 lambda。
【解决方案2】:

我认为目前不可能缩短此时间。一年前,C++ 委员会研究了http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3601.html 来解决这个问题,并鼓励作者在 C++14 发布后进一步追求它。

【讨论】:

  • FWIW,我一直在争论使用更紧凑的符号 template&lt;auto X&gt;(使用 decltype 检索类型部分)。我相信现在有牵引力,所以论文的修订有望表明这一点。
【解决方案3】:

您需要为要作为指针传递的任何函数类型创建一个 typedef,如下所示:

typedef int (*MyFunctionType)(int);

template <typename FunctionTypedef, typename ReturnType, typename ParameterType>
ReturnType callFunction(FunctionTypedef functionPointer, ParameterType i)
{
  static MyFunctionType myFunctionPointer = functionPointer;
  return (*functionPointer)(i);
}
int myFunction(int i)
{
}


int main()
{
  int i = 7;
  MyFunctionType myFunctionPointer = myFunction;
  return callFunction<MyFunctionType, int, int>(myFunctionPointer, i);
}

编辑:如果你想存储这些任意类型的函数指针,那么创建一个带有虚拟“调用函数”函数的基类,以及一个实现该函数的模板派生类。

【讨论】:

    猜你喜欢
    • 2012-12-27
    • 1970-01-01
    • 1970-01-01
    • 2017-06-14
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 2021-02-28
    • 2012-06-07
    相关资源
    最近更新 更多