2016-08-22 73 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 /。沒有函數類型的變量。 –

相關問題