接受std::函数的模板函数,该函数依赖于模板形参

C++11 Template function that takes a std::function which depends of template parameters

本文关键字:函数 函数依赖 依赖于 形参 接受 std      更新时间:2023-10-16

我正在尝试编写一个模板函数,该函数接受依赖于模板参数的std::function。不幸的是,编译器不能正确地推导出std::function的参数。下面是一些简单的示例代码:

#include <iostream>
#include <functional>
using namespace std;
void DoSomething( unsigned ident, unsigned param )
{
    cout << "DoSomething called, ident = " << ident << ", param = "  << param << "n";
}
template < typename Ident, typename Param >
void CallFunc( Ident ident, Param param, std::function< void ( Ident, Param ) > op )
{
    op( ident, param );
}
int main()
{
    unsigned id(1);
    unsigned param(1);
    // The following fails to compile
    // CallFunc( id, param, DoSomething );
    // this is ok 
    std::function< void ( unsigned, unsigned ) > func( DoSomething );
    CallFunc( id, param, func ); 
    return 0;
}

如果我用下面的语句调用模板:

CallFunc( id, param, DoSomething );

得到以下错误:

function-tpl.cpp:25: error: no matching function for call to CallFunc(unsigned int&, unsigned int&, void (&)(unsigned int, unsigned int))

如果我显式地创建一个正确类型的std::函数(或强制转换它),问题就解决了:

std::function< void ( unsigned, unsigned ) > func( DoSomething );
CallFunc( id, param, func );

我该如何编码,使显式临时不需要?

您需要将第三个函数参数作为其中模板参数的非推导上下文。然后,编译器将不会在不考虑所有隐式转换的情况下比较实参类型和形参类型(标准说,c++ 0x进一步澄清了这一点,对于没有模板形参的函数形参,所有隐式转换都被允许弥合差异)。

template < typename T > struct id { typedef T type; };
template < typename Ident, typename Param >
void CallFunc( Ident ident, Param param, 
               typename id<std::function< void ( Ident, Param ) >>::type op )
{
    op( ident, param );
}

你可以用boost::identity代替id。在c++ 0x和支持它的编译器中,您可以使用别名模板获得更具可读性的版本

template < typename T > using nondeduced = typename id<T>::type;

那么你的代码变成了简单的

template < typename Ident, typename Param >
void CallFunc( Ident ident, Param param, 
               std::function< nondeduced<void ( Ident, Param )> > op )
{
    op( ident, param );
}

但是GCC还不支持别名模板。

如果你使用的是模板,你可以完全避免使用std::function,除非出于某种原因你想要特别限制函数只能使用std::function:

template < typename Ident, typename Param, typename Func >
void CallFunc( Ident ident, Param param, Func op )
{
    op( ident, param );
}

您可以内联或使用bind进行转换。两者都不是特别漂亮,但它们都能完成工作:

CallFunc(id, param, std::function<void(unsigned, unsigned)>(DoSomething));

CallFunc(id, param, std::bind(DoSomething, std::placeholders::_1, std::placeholders::_2));