C++11变量标准::函数参数

jhkqcmku  于 2023-02-26  发布在  其他
关注(0)|答案(4)|浏览(155)

名为test的函数将std::function<>作为其参数。

template<typename R, typename ...A>
void test(std::function<R(A...)> f)
{
    // ...
}

但是,如果我执行以下操作:

void foo(int n) { /* ... */ }

// ...

test(foo);

编译器(gcc 4.6.1)显示为no matching function for call to test(void (&)(int))
要使test(foo)的最后一行能够正确编译和工作,我如何修改test()函数?在test()函数中,我需要f,类型为std::function<>
我的意思是,有没有什么模板技巧可以让编译器确定函数的签名(示例中为foo),并自动将其转换为std::function<void(int)>

编辑

我想让这也适用于lambdas(有状态的和无状态的)。

zengzsys

zengzsys1#

看起来您要使用重载

template<typename R, typename ...A>
void test(R f(A...))
{
    test(std::function<R(A...)>(f));
}

这个简单的实现将接受大多数(如果不是全部的话)你要传递的函数。外来的函数将被拒绝(比如void(int...))。更多的工作将给予你更多的通用性。

o0lyfsai

o0lyfsai2#

std::function实现了Callable接口,也就是说,它看起来像一个函数,但这并不意味着您应该要求可调用对象为std::function

template< typename F > // accept any type
void test(F const &f) {
    typedef std::result_of< F( args ) >::type R; // inspect with traits queries
}

鸭子类型是模板元编程中最好的策略。当接受模板参数时,不要指定,让客户端实现接口即可。
如果你真的需要一个std::function来重定向变量或类似的东西,并且你知道输入是一个原始函数指针,你可以分解一个原始函数指针类型并将其重新构造成一个std::function

template< typename R, typename ... A >
void test( R (*f)( A ... ) ) {
    std::function< R( A ... ) > internal( f );
}

现在用户不能传递一个std::function,因为它已经封装在函数中了,你可以保留你现有的代码作为另一个重载,并委托给它,但是要注意保持接口的简单。
至于有状态的lambda,我不知道如何处理这种情况,它们不能分解为函数指针,而且据我所知,参数类型不能被查询或推导,这些信息对于示例化std::function是必要的,不管是好是坏。

mccptt67

mccptt673#

这是一个老的,我似乎找不到太多关于同一主题,所以我想我会继续下去,并把一个说明。
根据GCC 4.8.2编写,进行以下工作:

template<typename R, typename... A>
R test(const std::function<R(A...)>& func)
{
    // ...
}

然而,你不能仅仅通过传递指针、lambda等来调用它。然而,下面两个例子都可以使用它:

test(std::function<void(int, float, std::string)>(
        [](int i, float f, std::string s)
        {
            std::cout << i << " " << f << " " << s << std::endl;
        }));

还有:

void test2(int i, float f, std::string s)
{
    std::cout << i << " " << f << " " << s << std::endl;
}

// In a function somewhere:
test(std::function<void(int, float, std::string)>(&test2));

这些方法的缺点应该非常明显:你必须为它们显式地声明std::函数,这看起来可能有点难看。
尽管如此,我还是把它和一个元组放在一起,这个元组被扩展来调用传入的函数,它工作了,只是需要更多一点显式地说明调用test函数的操作。
示例代码包括元组,如果你想玩它:http://ideone.com/33mqZA

utugiqy6

utugiqy64#

通常不建议按值接受std::function,除非你是在“二进制定界”(例如动态库,“opaque”API),因为你刚刚看到它们会对重载造成严重破坏。当一个函数确实按值接受std::function时,调用者通常会承担构造对象以避免重载问题的负担(如果函数确实重载的话)。
不管你写了一个模板,很可能你没有使用std::function(作为一个参数类型)来获得类型擦除的好处。如果你想检查任意的函子,那么你需要一些traits来实现。例如Boost。FunctionTypes有一些traits,比如result_typeparameter_types。一个最小的函数示例:

#include <functional>

#include <boost/function_types/result_type.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <boost/function_types/function_type.hpp>

template<typename Functor>
void test(Functor functor) // accept arbitrary functor!
{
    namespace ft = boost::function_types;

    typedef typename ft::result_type<Functor>::type result_type;
    typedef ft::parameter_types<Functor> parameter_types;
    typedef typename boost::mpl::push_front<
        parameter_types
        , result_type
    >::type sequence_type;
    // sequence_type is now a Boost.MPL sequence in the style of
    // mpl::vector<int, double, long> if the signature of the
    // analyzed functor were int(double, long)

    // We now build a function type out of the MPL sequence
    typedef typename ft::function_type<sequence_type>::type function_type;

    std::function<function_type> function = std::move(functor);
}

最后一点,我不建议在一般情况下对函子进行内省(例如,检查其结果类型和参数类型),因为这对多态函子根本不起作用。那么就没有“规范的”结果类型或参数类型。对于C++11,最好是“急切地”接受任何类型的函子,或者根据需要使用SFINAE或static_assert等技术来约束它们。稍后(当参数可用时)使用std::result_of检查给定参数集 * 的结果类型 *。期望预先约束的情况是当目的是将函子存储到例如std::function<Sig>的容器中时。
要体会我前面一段的意思,用多态函子测试上面的代码段就足够了。

相关问题