2015-12-15 66 views
3

我目前正在閱讀幾本書以抓住C++ 14的特性。我正在嘗試使用可變參數模板將參數綁定到函數。我知道如何使用std :: bind來做到這一點,但我也想用C++ 14 lambda表達式來實現這個功能,只是爲了常識和理解,以及任何可能的性能優勢。我讀過lambda表達式可以內聯,而std :: bind不能內聯,因爲它通過調用函數指針發生。C++ 14函數綁定的可變參數lambda捕獲

下面是從myFunctions.h代碼:

#include <functional> 

int simpleAdd(int x, int y) { 
    return x + y; 
} 

//function signatures 
template<class Func, class... Args> 
decltype(auto) funcBind(Func&& func, Args&&...args); 

template<class Func, class... Args> 
decltype(auto) funcLambda(Func&& func, Args&&...args); 

///////////////////////////////////////////////////////////////// 

//function definitions 
template<class Func, class... Args> 
inline decltype(auto) funcBind(Func&& func, Args&&... args) 
{ 
    return bind(forward<Func>(func), forward<Args>(args)...); 
} 

template<class Func, class ...Args> 
inline decltype(auto) funcLambda(Func && func, Args && ...args) 
{ //The error is caused by the lambda below: 
    return [func, args...]() { 
     forward<Func>(func)(forward<Args>(args)...); 
    }; 
} 

這裏是我運行的主要代碼:

#include<iostream> 
#include<functional> 
#include "myFunctions.h" 
using namespace std; 


int main() 
{ 
    cout << "Application start" << endl; 
    cout << simpleAdd(5,7) << endl; 

    auto f1 = funcBind(simpleAdd,3, 4); 
    cout << f1() << endl; 

    //error is occurring below 
    auto f2 = funcLambda(simpleAdd, 10, -2); 
    cout << f2() << endl; 

    cout << "Application complete" << endl; 

錯誤C2665 '的std ::前進':沒有2個重載可以轉換所有的參數類型

錯誤C2198 'INT(__cdecl &)(INT,INT)':呼叫

參數太少

我認爲錯誤可能發生在可變參數被轉發到lambda時,但我並不確定。

我的問題是我如何正確地制定這段代碼,以便我可以使用lambda捕獲函數及其參數,並在稍後調用它。

回答

4

我讀過lambdas可以內聯,而std :: bind不能內聯 ,因爲它通過調用函數指針進行。

如果您將simpleAdd傳遞給可以綁定參數的東西,那麼您是否使用bind並不重要。你認爲lambda與func捕獲什麼?這是一個函數指針。

lambda-vs-function-pointer的情況是關於編寫bind(simpleAdd, 2, 3)[] { return simpleAdd(2, 3); }。或者直接綁定類似[](auto&&...args) -> decltype(auto) { return simpleAdd(decltype(args)(args)...); }的lambda與綁定simpleAdd(它將使用函數指針)。


在任何情況下,實施它是令人驚訝的棘手。你不能使用by-reference捕獲,因爲事情很容易就會懸浮起來,你不能使用一個簡單的按值捕獲,因爲它總是會拷貝參數,即使是rvalues,也不能在init中進行包擴展-捕獲。

這遵循的std::bind語義(調用所述功能對象和傳遞所有綁定參數作爲左值),除了1)它不處理的佔位符或嵌套結合,和2)的函數調用操作總是const

template<class Func, class ...Args> 
inline decltype(auto) funcLambda(Func && func, Args && ...args) 
{ 
    return [func = std::forward<Func>(func), 
      args = std::make_tuple(std::forward<Args>(args)...)] { 
     return std::experimental::apply(func, args); 
    }; 
} 

cppreference具有執行std::experimental::apply

請注意,這可以解開reference_wrapper s,就像bind一樣,因爲make_tuple可以。

你的原代碼打破了,因爲args是在lambda的函數調用操作const(這是const默認情況下),以及forward結束試圖拋棄常量性。

+0

謝謝你的回答。使用lambda捕獲作爲使用std :: bind的複雜性比我期待的要複雜得多。從cppreference.com申請和調用,但我收到一些更多的編譯器錯誤:錯誤\t C2893 \t無法專門化func (_Callable &&,_ Types && ...)',以及錯誤\t C2975 \t'_Size':'std :: make_index_sequence'的無效模板參數,預期的編譯時常量表達式。稍後我會再看一遍,但現在我會堅持使用綁定。 –

0

您可以使用一個元組:

template<class Func, class ...Args> 
inline decltype(auto) funcLambda(Func && func, Args && ...args) 
{ //The error is caused by the lambda below: 
    auto tpl = make_tuple(std::forward<Args>(args)...); 

    //Use move just in case Args has move-only types. 
    return [func, tpl = move(tpl)]() { 
     apply(func, tpl); 
    }; 
} 

哪裏applydefined something like this

namespace detail { 
template <class F, class Tuple, std::size_t... I> 
constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>) 
{ 
    return f(std::get<I>(std::forward<Tuple>(t))...); 
} 
} // namespace detail 

template <class F, class Tuple> 
constexpr decltype(auto) apply(F&& f, Tuple&& t) 
{ 
    return detail::apply_impl(std::forward<F>(f), std::forward<Tuple>(t), 
     std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>>::value); 
} 

apply是圖書館TS版本之一的一個特徵。使用C++ 17,apply_impl可以調用invoke,這將適用於任何callable

+0

謝謝你的回答。應該捕獲func的lambda捕獲的第一部分實際上是否說「func = forward (func)」以確保您正確地轉發任何Rvalues?這應該也適用於apply_impl,而不是隻是「f(...」應該說「轉發(f)(...」。 –

+0

我也遇到編譯器錯誤,當我嘗試這種解決方案,其中之一類似於錯誤\t C2975 \t'_Size':'std :: make_index_sequence'的無效模板參數,預期的編譯時常量表達式和錯誤\t C2198 \t'int(__cdecl * const )(int,int)':調用的參數太少 –