25

一个名为的函数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(有状态的和无状态的)做这项工作。

4

4 回答 4

14

看起来你想使用重载

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

这个简单的实现将接受您尝试传递的大部分(如果不是全部)功能。外来函数将被拒绝(如void(int...))。更多的工作会给你更多的通用性。

于 2012-02-11T17:19:22.843 回答
10

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

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

现在用户不能传递 astd::function因为它已被封装在函数中。您可以将现有代码保留为另一个重载并委托给它,但请注意保持接口简单。

至于有状态的 lambda,我不知道如何处理这种情况。它们不会分解为函数指针,据我所知,不能查询或推断参数类型。std::function无论好坏,此信息对于实例化都是必需的。

于 2012-02-12T14:16:22.810 回答
4

通常不建议std::function按值接受,除非您处于“二进制分隔”(例如动态库、“不透明”API),因为正如您刚刚看到的那样,它们对重载造成严重破坏。当一个函数实际上确实采用了std::function值时,调用者通常有责任构造对象以避免重载问题(如果函数完全重载)。

但是,由于您已经编写了模板,因此很可能您没有使用std::function(作为参数类型)来获得类型擦除的好处。如果您想做的是检查任意函子,那么您需要一些特征。例如 Boost.FunctionTypes 具有result_type和等特征parameter_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);
}

作为最后一点,我不建议在一般情况下内省仿函数(即刺激其结果类型和参数类型),因为这根本不适用于多态仿函数。考虑几个重载operator():然后没有“规范”结果类型或参数类型。使用 C++11,最好“急切地”接受任何类型的函子,或者使用 SFINAE 等技术或static_assert根据需要限制它们,然后(当参数可用时)用于std::result_of检查给定集合的结果类型论据。需要预先进行约束的情况是,当目标是将函子存储到例如std::function<Sig>.

要了解我上一段的意思,使用多态函子测试上面的代码片段就足够了。

于 2012-02-12T07:52:38.150 回答
4

这是一个旧的,我似乎在同一个主题上找不到太多,所以我想我会继续做笔记。

在 GCC 4.8.2 上编译,以下工作:

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

但是,您不能只通过传入指针、lambda 等来调用它。但是,以下 2 个示例都可以使用它:

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::function,这可能看起来有点难看。

尽管如此,我还是把它和一个被扩展以调用传入函数的元组一起扔掉了,它可以工作,只需要更明确地说明你正在做什么调用测试函数。

示例代码包括元组的东西,如果你想玩它:http: //ideone.com/33mqZA

于 2014-05-31T12:51:15.407 回答