Boost Part III. 函数对象与高级编程 Library 10. Lambda 用法
讓你的函數(shù)對(duì)象可以與Boost.Lambda 一起使用
不是所有的表達(dá)式都適合使用 lambda 表達(dá)式,復(fù)雜的表達(dá)式更適合使用普通的函數(shù)對(duì)象,而且會(huì)多次重用的表達(dá)式也應(yīng)該成為你代碼中的一等公民。它們應(yīng)該被收集為一個(gè)可重用函數(shù)對(duì)象的庫(kù)。但是, 你也可能想把這些函數(shù)對(duì)象用在lambda 表達(dá)式中,你希望它們可以與 Lambda 一起使用;不是所有函數(shù)對(duì)象都能做到。問題是函數(shù)對(duì)象的返回類型不能象普通函數(shù)那樣被推斷出來(lái);這是語(yǔ)言的固有限制。但是,有一個(gè)定義好的方法來(lái)把這個(gè)重 要的信息提供給Lambda 庫(kù),以使得 bind 表達(dá)式更加干凈。作為這個(gè)問題的一個(gè)例子,我們看以下函數(shù)對(duì)象:
template <typename T> class add_prev {T prev_; public:add_prev() : prev_(0){}T operator()(T t) {prev_+=t;return prev_;} };對(duì)于這樣一個(gè)函數(shù)對(duì)象,lambda 表達(dá)式不能推斷出返回類型,因此以下例子不能編譯。
#include <iostream> #include <algorithm> #include <vector> #include "boost/lambda/lambda.hpp" #include "boost/lambda/bind.hpp" int main() {using namespace boost::lambda;std::vector<int> vec;vec.push_back(5);vec.push_back(8);vec.push_back(2);vec.push_back(1);add_prev<int> ap;std::transform(vec.begin(),vec.end(),vec.begin(),bind(var(ap),_1)); }問題在于對(duì) transform 的調(diào)用。
std::transform(vec.begin(),vec.end(),vec.begin(),bind(var(ap),_1));當(dāng)綁定器被實(shí)例化時(shí),返回類型推斷的機(jī)制被使用…而且失敗了。因此,這段程序不能通過編譯,你必須顯式地告訴 bind 返回類型是什么,象這樣:
std::transform(vec.begin(),vec.end(),vec.begin(),bind<int>(var(ap),_1));這是為 lambda 表達(dá)式顯式設(shè)置返回類型的正常格式的縮寫,它等價(jià)于這段代碼。
std::transform(vec.begin(),vec.end(),vec.begin(),ret<int>(bind<int>(var(ap),_1)));這并不是什么新問題;對(duì)于在標(biāo)準(zhǔn)庫(kù)算法中使用函數(shù)對(duì)象都有同樣的問題。在標(biāo)準(zhǔn)庫(kù)中,解決的方法是增加typedefs 來(lái)表明函數(shù)對(duì)象的返回類型及參數(shù)類型。標(biāo)準(zhǔn)庫(kù)還提供了助手類來(lái)完成這件事,即類模板unary_function 和 binary_function,要讓我們的例子類add_prev 成為合適的函數(shù)對(duì)象,可以通過定義所需的 typedefs (對(duì)于一元函數(shù)對(duì)象,是argument_type 和 result_type,對(duì)于二元函數(shù)對(duì)象,是first_argument_type,second_argument_type, 和 result_type),也可以通過派生自unary_function/binary_function 來(lái)實(shí)現(xiàn)。
template <typename T> class add_prev : public std::unary_function<T,T>這對(duì)于lambda 表達(dá)式是否也足夠好了呢?我們可以簡(jiǎn)單地復(fù)用這種方法以及我們已有的函數(shù)對(duì)象嗎?唉,答案是否定的。這種typedef 方法有一個(gè)問題:對(duì)于泛化的調(diào)用操作符,當(dāng)返回類型或參數(shù)類型依賴于模板參數(shù)時(shí)會(huì)怎么樣?或者,當(dāng)存在多個(gè)重載的調(diào)用操作符時(shí)會(huì)怎么樣?由于語(yǔ)言支持模板的typedefs, 這些問題可以解決,但是現(xiàn)在不是這樣的。這就是為什么 Boost.Lambda 需要一個(gè)不同的方法,即一個(gè)名為 sig 的嵌套泛型類。為了讓返回類型推斷可以和add_prev 一起使用,我們象下面那樣定義一個(gè)嵌套類型 sig :
template <typename T> class add_prev :public std::unary_function<T,T> {T prev_; public:template <typename Args> class sig {public:typedef T type;}; // Rest of definition模板參數(shù) Args 實(shí)際上是一個(gè) tuple,包含了函數(shù)對(duì)象(第一個(gè)元素)和調(diào)用操作符的參數(shù)類型。在這個(gè)例子中,我們不需要這些信息,返回類型和參數(shù)類型都是T. 使用這個(gè)改進(jìn)版本的 add_prev,再不需要在 lambda 表達(dá)式中使用返回類型推斷的縮寫,因此我們最早那個(gè)版本的代碼現(xiàn)在可以編譯了。
std::transform(vec.begin(),vec.end(),vec.begin(),bind(var(ap),_1));我們?cè)賮?lái)看看tuple 作為 sig 的模板參數(shù)是如何工作的,來(lái)看另一個(gè)有兩個(gè)調(diào)用操作符的函數(shù)對(duì)象,其中一個(gè)版本接受一個(gè)int 參數(shù),另一個(gè)版本接受一個(gè) const std::string引用。我們必須要解決的問題是,"如果傳遞給 sig模板的 tuple 的第二個(gè)元素類型為 int,則設(shè)置返回類型為 std::string; 如果傳遞給 sig 模板的 tuple 的第二個(gè)元素類型為 std::string, 則設(shè)置返回類型為 double"。為此,我們?cè)黾右粋€(gè)類模板,我們可以對(duì)它進(jìn)行特化并在add_prev::sig 中使用它。
template <typename T> class sig_helper {}; // The version for the overload on int template<> class sig_helper<int> { public:typedef std::string type; }; // The version for the overload on std::string template<> class sig_helper<std::string> { public:typedef double type; }; // The function object class some_function_object {template <typename Args> class sig {typedef typename boost::tuples::element<1,Args>::typecv_first_argument_type;typedef typenameboost::remove_cv<cv_first_argument_type>::typefirst_argument_type;public:// The first argument helps us decide the correct versiontypedef typenamesig_helper<first_argument_type>::type type;};std::string operator()(int i) const {std::cout << i << '\n';return "Hello!";}double operator()(const std::string& s) const {std::cout << s << '\n';return 3.14159265353;} };這里有兩個(gè)重要的部分要討論:首先是助手類sig_helper, 它由類型 T特化。這個(gè)類型可以是 int 或 std::string,依賴于要使用哪一個(gè)重載版本的調(diào)用操作符。通過對(duì)這個(gè)模板進(jìn)行全特化,來(lái)定義正確的 typedeftype。第二個(gè)要注意的部分是 sig 類,它的第一個(gè)參數(shù)(即tuple 的第二個(gè)元素)被取出,并去掉所有的 const 或 volatile 限定符,結(jié)果類型被用于實(shí)例化正確版本的sig_helper 類,后者具有正確的 typedef type.這是為我們的類定義返回類型的一種相當(dāng)復(fù)雜(但是必須!)的方法,但是多數(shù)情況下,通常都只有一個(gè)版本的調(diào)用操作符;所以正確地增加嵌套sig 類是一件普通的工作。
我們的函數(shù)對(duì)象可以在 lambda 表達(dá)式中正確使用是很重要的,在需要時(shí)定義嵌套sig 類是一個(gè)好主意;它很有幫助。
附代碼:
#include <iostream> #include <algorithm> #include <vector> #include <string> #include "boost/lambda/lambda.hpp" #include "boost/lambda/bind.hpp" template <typename T> class sig_helper {}; // The version for the overload on int template<> class sig_helper<int> { public:typedef std::string type; }; // The version for the overload on std::string template<> class sig_helper<std::string> { public:typedef double type; }; // The function object class some_function_object {template <typename Args> class sig {typedef typename boost::tuples::element<1, Args>::typecv_first_argument_type;typedef typenameboost::remove_cv<cv_first_argument_type>::typefirst_argument_type;public:// The first argument helps us decide the correct versiontypedef typenamesig_helper<first_argument_type>::type type;}; public:std::string operator()(int i) const {std::cout << i << '\n';return "Hello!";}double operator()(const std::string& s) const {std::cout << s << '\n';return 3.14159265353;} }; template <typename T, typename Operation> void for_all(T& t, Operation Op) {std::for_each(t.begin(), t.end(), Op); } template <typename T> void print(std::vector<T>& vec) {//using namespace boost::lambda;for_all(vec, std::cout << boost::lambda::_1 << '\n'); } int main() {using namespace boost::lambda;{std::vector<std::string> vec;vec.push_back("my darling");vec.push_back("so sweet...");std::vector<double> vec2;vec2.push_back(1);vec2.push_back(2);some_function_object sfo;std::transform(vec.begin(),vec.end(),vec2.begin(),bind(var(sfo), _1));print(vec2);}{std::vector<std::string> vec;vec.push_back("my darling");vec.push_back("so sweet...");std::vector<int> vec2;vec2.push_back(1);vec2.push_back(2);some_function_object sfo;std::transform(vec2.begin(),vec2.end(),vec.begin(),bind(var(sfo), _1));print(vec);} }總結(jié)
以上是生活随笔為你收集整理的Boost Part III. 函数对象与高级编程 Library 10. Lambda 用法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: checked_delete问题: Be
- 下一篇: Boost Part III. 函数对象