2016-08-22 92 views
13

我在C++ 11的实验中偶然发现了这个问题。我发现这是一个明显的解决方案,但我一直无法在其他地方找到任何其他示例,所以我担心有些事情我错过了。作为参数传递函数的std :: function的替代方法(回调等)

的做法我指的是(在“addAsync”功能):

#include <thread> 
#include <future> 
#include <iostream> 
#include <chrono> 

int addTwoNumbers(int a, int b) { 
    std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl; 

    return a + b; 
} 

void printNum(std::future<int> future) { 
    std::cout << future.get() << std::endl; 
} 

void addAsync(int a, int b, auto callback(std::future<int>) -> void) { //<- the notation in question 
    auto res = std::async(std::launch::async, addTwoNumbers, a, b); 

    if (callback) //super straightforward nullptr handling 
     return callback(std::move(res)); 
} 

int main(int argc, char** argv) { 
    addAsync(10, 10, [](std::future<int> number) { //lambda functions work great 
     addAsync(number.get(), 20, [](std::future<int> number) { 
      addAsync(893, 4387, printNum); //as do standard functions 
      addAsync(2342, 342, nullptr); //executes, sans callback 

      std::cout << number.get() << std::endl; 
     }); 
    }); 

    std::cout << "main thread: " << std::this_thread::get_id() << std::endl; 

    return 0; 
} 

是它认为不好的做法,或者是不可移植的(我只是尝试在MSVC++ 2015年) ?另外,编译器如何处理这个问题。通过转换为std :: function?

我很想继续在我的项目中使用它,因为它显然在“签名”中声明了所需的参数类型和返回类型,接受可选的nullptr,并且似乎“只是工作”(我意识到这些都是C++中的最后一句话)。

+2

@Amadeus:也许这是以问号结束的句子。关于SO的问题有很多。这不是其中的一个。 –

+3

该参数被声明为具有函数类型'void(std :: future )',该函数被调整为函数'void(*)(std :: future )'的指针。 –

回答

11

你使用的原始指针的功能。

不同于std::function,这不会与捕捉的λ工作,或与std::bind结果,或者使用实现operator()一般类型。

+0

啊,我希望它不会成为一个原始指针。非常感谢澄清。在许多情况下,限制re:捕获是一种破坏行为,为了一致性,我认为我会坚持使用std :: function和std :: bind。我会将其标记为答案! –

1

替代标准::函数传递函数作为参数”

一种选择将是一个函数指针(包括成员函数指针),但std::function就是这么好得多(IMO)。

14

auto callback(std::future<int>) -> void是类型为void(std::future<int>)的实体的声明,称为callback。当列为参数时,编译器会将其调整为void(*)(std::future<int>)类型的指针函数。

您的lambda是无状态的,因此可以隐式转换为函数指针。

一旦你添加一个不平凡的拍摄,你的代码将停止编译:

[argc](std::future<int> number) { 
    std::cout << argc << '\n'; 

...

现在,忽略你的问题的内容来看,在标题...

std::function价格适中,因为它是一种价值型而非视图型。作为一种价值型,它实际上复制了它的论点。

可以解决这个问题通过在std::ref包装调用对象,但如果你想说出“我不会让这个函数对象比这个电话的时间更长”,您可以按如下编写一个function_view类型:

template<class Sig> 
struct function_view; 

template<class R, class...Args> 
struct function_view<R(Args...)> { 
    void* ptr = nullptr; 
    R(*pf)(void*, Args...) = nullptr; 

    template<class F> 
    using pF = decltype(std::addressof(std::declval<F&>())); 

    template<class F> 
    void bind_to(F& f) { 
    ptr = (void*)std::addressof(f); 
    pf = [](void* ptr, Args... args)->R{ 
     return (*(pF<F>)ptr)(std::forward<Args>(args)...); 
    }; 
    } 
    // when binding to a function pointer 
    // even a not identical one, check for 
    // null. In addition, we can remove a 
    // layer of indirection and store the function 
    // pointer directly in the `void*`. 
    template<class R_in, class...Args_in> 
    void bind_to(R_in(*f)(Args_in...)) { 
    using F = decltype(f); 
    if (!f) return bind_to(nullptr); 
    ptr = (void*)f; 
    pf = [](void* ptr, Args... args)->R{ 
     return (F(ptr))(std::forward<Args>(args)...); 
    }; 
    } 
    // binding to nothing: 
    void bind_to(std::nullptr_t) { 
    ptr = nullptr; 
    pf = nullptr; 
    }  
    explicit operator bool()const{return pf;} 

    function_view()=default; 
    function_view(function_view const&)=default; 
    function_view& operator=(function_view const&)=default; 

    template<class F, 
    std::enable_if_t< !std::is_same<function_view, std::decay_t<F>>{}, int > =0, 
    std::enable_if_t< std::is_convertible< std::result_of_t< F&(Args...) >, R >{}, int> = 0 
    > 
    function_view(F&& f) { 
    bind_to(f); // not forward 
    } 

    function_view(std::nullptr_t) {} 

    R operator()(Args...args) const { 
     return pf(ptr, std::forward<Args>(args)...); 
    } 
}; 

live example

这也是有用的,因为它是一种严格比std::function更简单的类型删除,所以它可能是教育过去。

+0

谢谢你扩大在搜索器的答案。编译器手持的另一个例子。 –

+1

s/variable/entity /。没有函数类型的变量。 –