简体   繁体   English

C++11 可变参数 std::function 参数

[英]C++11 variadic std::function parameter

A function named test takes std::function<> as its parameter.名为test的函数采用std::function<>作为其参数。

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

But, if I do the following:但是,如果我执行以下操作:

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

// ...

test(foo);

Compiler(gcc 4.6.1) says no matching function for call to test(void (&)(int)) . Compiler(gcc 4.6.1) 说no matching function for call to test(void (&)(int))

To make the last line test(foo) compiles and works properly, how can I modify the test() function?为了使最后一行test(foo)编译并正常工作,我该如何修改test()函数? In test() function, I need f with type of std::function<> .test()函数中,我需要f类型为std::function<>

I mean, is there any template tricks to let compiler determine the signature of function( foo in example), and convert it to std::function<void(int)> automatically?我的意思是,是否有任何模板技巧可以让编译器确定函数的签名(例如foo ),并将其自动转换为std::function<void(int)>

EDIT编辑

I want to make this work for lambdas(both stated and stateless) as well.我也想为 lambdas(声明的和无状态的)做这项工作。

It looks like you want to use overloading看起来你想使用重载

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

This simple implementation will accept most if not all the functions you will try to pass.这个简单的实现将接受您将尝试传递的大部分功能。 Exotic functions will be rejected (like void(int...) ).外来函数将被拒绝(如void(int...) )。 More work will give you more genericity.更多的工作会给你更多的通用性。

std::function implements the Callable interface, ie it looks like a function, but that doesn't mean you should require callable objects to be std::function s. 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
}

Duck typing is the best policy in template metaprogramming.鸭子类型是模板元编程中的最佳策略。 When accepting a template argument, be unspecific and just let the client implement the interface.接受模板参数时,不要具体,让客户端实现接口。

If you really need a std::function for example to re-target the variable or something crazy like that, and you know the input is a raw function pointer, you can decompose a raw function pointer type and reconsitute it into a std::function .例如,如果您确实需要std::function来重新定位变量或类似的东西,并且您知道输入是原始函数指针,则可以分解原始函数指针类型并将其重组为std::function

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

Now the user can't pass a std::function because that has been encapsulated within the function.现在用户无法传递std::function因为它已封装在函数中。 You could keep your existing code as another overload and just delegate to that, but be careful to keep interfaces simple.您可以将现有代码保留为另一个重载并仅委托给它,但要注意保持接口简单。

As for stateful lambdas, I don't know how to handle that case.至于有状态的 lambda,我不知道如何处理这种情况。 They don't decompose to function pointers and as far as I know the argument types cannot be queried or deduced.它们不会分解为函数指针,据我所知,无法查询或推断参数类型。 This information is necessary to instantiate std::function , for better or worse.无论好坏,此信息对于实例化std::function都是必要的。

It's usually ill-advised to accept std::function by value unless you are at 'binary delimitation' (eg dynamic library, 'opaque' API) since as you've just witnessed they play havoc with overloading.通常不建议按值接受std::function ,除非您处于“二进制分隔”(例如动态库、“不透明”API),因为正如您刚刚目睹的那样,它们在重载方面造成了严重破坏。 When a function does in fact take an std::function by value then it's often the burden of the caller to construct the object to avoid the overloading problems (if the function is overloaded at all).当一个函数确实按值使用std::function ,调用者通常有责任构造对象以避免重载问题(如果函数完全重载)。

Since however you've written a template, it's likely the case that you're not using std::function (as a parameter type) for the benefits of type-erasure.然而,由于您编写了一个模板,因此很可能您没有使用std::function (作为参数类型)来获得类型擦除的好处。 If what you want to do is inspecting arbitrary functors then you need some traits for that.如果您想做的是检查任意函子,那么您需要一些特征。 Eg Boost.FunctionTypes has traits such as result_type and parameter_types .例如 Boost.FunctionTypes 具有诸如result_typeparameter_types A minimal, functional example:一个最小的功能示例:

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

As a final note, I do not recommend introspecting functors (ie prodding for their result type and argument types) in the general case as that simply don't work for polymorphic functors.最后一点,我不建议在一般情况下内省函子(即刺激它们的结果类型和参数类型),因为这根本不适用于多态函子。 Consider several overloaded operator() : then there is no 'canonical' result type or argument types.考虑几个重载的operator() :那么就没有“规范的”结果类型或参数类型。 With C++11 it's better to 'eagerly' accept any kind of functor, or constrain them using techniques like SFINAE or static_assert depending on the needs, and later on (when parameters are available) to use std::result_of to inspect the result type for a given set of arguments .使用 C++11,最好“热切地”接受任何类型的函子,或者根据需要使用 SFINAE 或static_assert等技术约束它们,然后(当参数可用时)使用std::result_of来检查结果输入一组给定的参数 A case where constraining up front is desirable is when the aim is to store functors into eg a container of std::function<Sig> .需要预先约束的情况是当目标是将函子存储到例如std::function<Sig>的容器中时。

To get a taste of what I mean by the previous paragraph it's enough to test the above snippet with polymorphic functors.为了了解我上一段的意思,用多态函子测试上面的代码片段就足够了。

This is an old one, and I can't seem to find much on the same topic, so I thought I would go ahead and put in a note.这是一个旧的,我似乎无法找到关于同一主题的太多内容,所以我想我会继续做一个笔记。

Compiled on GCC 4.8.2, the following works:在 GCC 4.8.2 上编译,以下工作:

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

However, you can't just call it by passing in your pointers, lambdas, etc. However, the following 2 examples both work with it:但是,您不能仅通过传入指针、lambda 等来调用它。但是,以下 2 个示例都可以使用它:

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

Also:还:

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

The downside of these should stand out pretty obviously: you have to explicitly declare the std::function for them, which might look a little bit ugly.这些的缺点应该很明显:您必须为它们显式声明 std::function ,这可能看起来有点难看。

That said, though, I threw that together with a tuple that gets expanded to call the incoming function, and it works, just requiring a little bit more of an explicitly saying what you're doing calling the test function.尽管如此,我将它与一个可以扩展以调用传入函数的元组放在一起,并且它可以工作,只需要多一点明确说明您正在调用测试函数的内容。

Example code including the tuple thing, if you want to play with it: http://ideone.com/33mqZA包含元组的示例代码,如果你想玩它: http : //ideone.com/33mqZA

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM