【问题标题】:How to make a call to a template function less verbose如何使对模板函数的调用不那么冗长
【发布时间】:2015-03-09 08:20:17
【问题描述】:

有一个功能

template <class ...T>
void foo(std::function<void(T...)> callback);

我将回调传递到其中。

我想做类似的事情

foo(bar);

例如,bar 在哪里

void bar(int a, long b, double c, float d);

但这给了我

error: no matching function for call to bar(void (&)(int, long int, double, float))

我必须打电话给foo

foo(std::function<void(int, long, double, float)>(bar));

这太冗长了。甚至

foo<int, long, double, float>(bar);

会更好。

foo(bar);

会很理想。

无论如何,我怎样才能让foo 的调用不那么冗长?

编辑:foo 的声明必须保持不变。

【问题讨论】:

  • foo&lt;int, long, double, float&gt;(bar) 应该可以工作(请注意,您在 Q 中使用了错误的类型)。你确定不是吗?您还可以展示您最终如何使用此回调吗?也许将它不强制传递给std::function 可能是一个解决方案。
  • 我看不到一个用例,因为模板不能在不知道参数内容的情况下调用函数。如果模板只转发函数,则该模板的目标也必须处理它。你能给我们一个真实世界的用例来解决这个问题吗?我的想法是,您可以从如何在模板中使用它们的知识中使模板参数可见。
  • template &lt;class F&gt; void foo(F callback); 有什么问题?你避免你的问题,不支付类型擦除
  • 哦,对了,我会修正类型。当我在 SO 示例中使用不同的代码时,从原始代码复制粘贴。 @Angew 它不起作用,对我吠叫 error: no matching function for call
  • @PiotrS。他们帮助我好吧,毫无疑问!事实上,foo 函数将填充了网络接收数据的缓冲区拆分为字节块,这样每个块都是其每个可变参数的sizeof,并将这些块应用为callback 的参数,调用@ 987654339@和他们一起!所以我们分割网络缓冲区的方式只取决于callback的签名!这种疯狂只有使用可变参数模板才有可能! (哦,别担心,我还有一些类型安全机制)。只是我想让函数调用不那么冗长/显式。

标签: c++ templates


【解决方案1】:

我会编写一个包装器函数,将函数指针转换为std::function 包装器:

template <typename... T>
void foo(std::function<void (T...)> f) {}

template <typename... T>
void foo(void (*f)(T...)) {
    foo(std::function<void (T...)>(f));
}

foo() 可以任意调用:

void bar(int,double) {}

void foo_caller() {
    foo(std::function<void (int,double)>(bar));
    foo(bar);
}

附录:非静态成员函数包装器

同样的方法可以用于指向成员的函数——只需添加另一个重载:

template <typename C,typename... T>
void foo(void (C::*f)(T...)) {
    foo(std::function<void (C *,T...)>(f));
}

注意成员函数的this 指针的额外第一个参数。用法类似:

struct quux {
    void mf(char *,double) {}
};

void foo_caller() {
    foo(&quux::mf);
}

【讨论】:

  • 如果bar 是一个非静态类方法,有什么办法可以使这个工作?
【解决方案2】:

如果你知道你会将一个普通的函数指针传递给foo,而不仅仅是任何C++11 lambda,你可以将foo重新定义为:

template <class ...T>
void foo(void(*callback)(T...)) {
   // .....
}

如果你想支持 lambdas,你可以更通用的类型

template <class LambdaType>
void foo(LambdaType callback) {
   // .....
}

这种方法的缺点是,如果您传递的不是函数或 lambda,您将收到来自 foo 内部的奇怪模板错误消息。


使用您的原始解决方案,编译器在将T...int, long, double, float 匹配时出现问题,可能是因为它是嵌套类型。

如果我告诉你将void(int, double) 匹配到MyTempalte&lt;T...&gt;,你不会知道我打算用int, double 替换T...,因为你不知道MyTemplate 对其参数做了什么。也许MyTemplate 首先对其模板参数做了一些奇怪的事情?

同样,编译器不知道如何将std::function 模板参数匹配到您的函数指针。

【讨论】:

    【解决方案3】:

    如果您的foo 定义不是一成不变的,可以将其更改为

    #include <functional>
    
    template <class Ret, class ...T>
    void foo(Ret callback(T... params))
    {
    }
    
    void bar(int a, long b, double c, float d){}
    
    int main() 
    {
        foo(bar);
    }
    

    【讨论】:

    • 这不是 OP 所要求的
    • @PiotrS。我虽然这正是 OP 所要求的,但没有说要保持 foo 的声明固定。
    • 现在他/她做到了,您建议添加返回类型是解决方案,这与实际问题无关
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-05
    • 1970-01-01
    • 2018-02-13
    • 2017-02-23
    • 1970-01-01
    • 2013-10-02
    • 2015-10-31
    相关资源
    最近更新 更多