• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            李錦俊(mybios)的blog

            游戲開發 C++ Cocos2d-x OpenGL DirectX 數學 計算機圖形學 SQL Server

              C++博客 :: 首頁 :: 聯系 :: 聚合  :: 管理
              86 Posts :: 0 Stories :: 370 Comments :: 0 Trackbacks

            公告

            QQ:30743734
            EMain:mybios@qq.com

            常用鏈接

            留言簿(16)

            我參與的團隊

            最新隨筆

            搜索

            •  

            積分與排名

            • 積分 - 370231
            • 排名 - 67

            最新評論

            閱讀排行榜

            評論排行榜

            從 STL 出現到現在已經這么多年了,泛型算法是它的重要組成,也是其中最“看起來很美”的東西之一。然而在真實的程序設計中,它往往成為程序員的心頭一痛,因為一旦要用 for_each ,accumulate 之類的算法做一些稍微復雜一點的事情,你就會發現自己一個頭變成兩個大。

            從 STL 出現到現在已經這么多年了,泛型算法是它的重要組成,也是其中最“看起來很美”的東西之一。然而在真實的程序設計中,它往往成為程序員的心頭一痛,因為一旦要用 for_each ,accumulate 之類的算法做一些稍微復雜一點的事情,你就會發現自己一個頭變成兩個大。即便是有了 boost::bind 和 boost.lambda 的幫助,事情也仍然是撲朔迷離,求助于 comp.lang.c++ 雖然有用,但是又何嘗不是一種無奈。好了,現在我開始收集一些來自 comp.lang.c++(.moderated) 的解答,希望日后對自己和他人有用。

            =================================================================================

            預備問題(算是第0個問題吧):如何對一個 STL 容器內的所有元素做某件事情?

            這取決于你要做什么,以及容器是什么。如果是 vector, list 這樣的容器,而你要把它們全部 cout 出來,當前的標準 STL 解法是這樣的:

            #include <iostream>
            #include <algorithm>
            #include <vector>
            #include <iterator>
            #include <string>

            int main()
            {
            ? std::vector<std::string> vect;
            ? vect.push_back("Hello");
            ? vect.push_back(", ");
            ? vect.push_back("world!");
            ?
            ? std::copy( vect.begin(), vect.end(),
            ??? std::ostream_iterator<std::string>(std::cout) );
            }

            基本上,這算是一個“過得去”的方案,但是有點問題:
            1. 對于不熟悉這個固定用法的人,cout 所有元素所首要考慮算法是 for_each,而不是 copy ,事實上,for_each 也是最符合我們慣常邏輯的算法,因為在不使用 STL 算法的時候,我們使用 for 循環來干這件事。
            2. 可讀性不太良好,ostream_iterator 的使用有點 tricky ,而且也不能用于做其他的事情。

            我想熟悉 boost? 的人已經知道我下面要說什么了,因為用 boost.lambda 做這件事情的確非常漂亮:

            #include <iostream>
            #include <algorithm>
            #include <vector>
            #include <string>

            #include <boost/lambda/lambda.hpp>
            #include <boost/lambda/bind.hpp>

            using namespace boost::lambda;

            int main()
            {
            ? std::vector<std::string> vect;
            ? vect.push_back("Hello");
            ? vect.push_back(", ");
            ? vect.push_back("world!");
            ?
            ? std::for_each( vect.begin(), vect.end(), std::cout << _1 );
            }

            這和前面的程序一樣,輸出我們熟悉的 Hello, world! 。直觀、優雅而且容易修改,例如,如果你不是想要輸出它們的值,而是想要輸出它們的長度,只需要做很少的修改:

            std::for_each( vect.begin(), vect.end(), std::cout << bind(&std::string::length, _1) << "\n" );

            輸出
            5
            2
            6

            bind 的作用是把 lambda 表達式綁定到一個函數或者一個數據成員,在這里的意思,就是對于每一個 string ,都調用其 length() 方法。_1 同樣也可以成為賦值的對象,例如,先執行

            std::for_each( vect[0].begin(), vect[0].end(), _1 = bind(&toupper, _1) );

            然后再把這些 string 輸出,你會得到

            HELLO, world!

            因為那一句對 "Hello" 中的每一個字母調用 toupper ,并把結果寫回。

            =================================================================================

            第一個問題:如何對一個 map 中所有的 key 或者 value 做某件事情?

            當然,這還是取決于你要做的是什么。手寫 for 循環當然是萬能的,但是現在有了那么多的泛型算法,我們可以考慮其他的方案了(這也是眾多 C++ Gurus 推薦的思維方式)如果是把所有的 value 全部 cout 出來,用 boost.lambda 配合 for_each 還是比較優雅的(雖然沒有像 vector 和 list 那樣的優雅):

            #include <iostream>
            #include <algorithm>
            #include <map>
            #include <string>

            #include <boost/lambda/lambda.hpp>
            #include <boost/lambda/bind.hpp>

            using namespace boost::lambda;

            int main()
            {
            ? std::map<int, std::string> strs;
            ? strs[0] = "Hello";
            ? strs[1] = ", ";
            ? strs[2] = "world";
            ? strs[3] = "!";
            ?
            ? std::for_each( strs.begin(), strs.end(),
            ??? std::cout << bind(&std::map<int, std::string>::value_type::second, _1) );
            }

            這樣的輸出如我們所料,就是 Hello, world!

            如果想要把 key 也輸出,道理是一樣的,只需要這樣:

            ? std::for_each( strs.begin(), strs.end(),
            ??? std::cout << bind(&std::map<int, std::string>::value_type::second, _1) << '\t'
            ??????????????????? << bind(&std::map<int, std::string>::value_type::first, _1) << '\n'
            ? );

            其結果是:

            Hello?? 0
            ,??????????? 1
            world?? 2
            !??????????? 3

            因此,對于一個 map 中的 value 的操作往往可以依法炮制,如果我們想要在其中尋找 value 為 “world" 的那一個元素,并且輸出它的 key ,只需要一句話:

            ? std::cout <<
            ??? std::find_if( strs.begin(), strs.end(),
            ????? bind(&std::map<int, std::string>::value_type::second, _1) == "world" )->first;

            STL 算法 find_if 接受的第三個參數是一個 prediate ,而生成這種臨時的 functor 正是 lambda 的拿手好戲。上面的這句話也可以用 boost::bind 來做,只需要更改 include 和 using namespace ,代碼本身無需更改。而如果你不借助于它們,你只有手寫循環或者自己寫一個 predicate 。

            當情況變得復雜一些的時候,lambda 的用法也變得更加有趣了:

            #include <iostream>
            #include <algorithm>
            #include <map>
            #include <string>

            #include <boost/lambda/lambda.hpp>
            #include <boost/lambda/bind.hpp>

            using namespace boost::lambda;

            class Person
            {
            public:
            ? Person(){}
            ? Person(const std::string& name) : name_(name){}
            ?
            ? std::string Name()
            ? { return name_; }
            ?
            private:
            ? std::string name_;
            };

            int main()
            {
            ? std::map<int, Person> persons;
            ? persons[123] = Person("Amy");
            ? persons[234] = Person("Ralph");
            ? persons[345] = Person("Simon");
            ? persons[456] = Person("Maggie");

            ? std::for_each( persons.begin(), persons.end(),
            ??? std::cout << bind(&std::map<int, Person>::value_type::first, _1) << '\t'
            ??????????????????? << bind(&Person::Name,
            ??????????????????????????? bind(&std::map<int, Person>::value_type::second, _1)) << '\n'
            ? );

            ? std::cout << "Ralph's Id is: " <<
            ??? std::find_if( persons.begin(), persons.end(),
            ????? bind(&Person::Name,
            ??????? bind(&std::map<int, Person>::value_type::second, _1)) == "Ralph" )->first;
            }

            這里 map 的 value 元素不再是一個單純的 string,我們要輸出的是這個 value 的 Name() ,幸好 lambda 的綁定可以級聯,所以我們仍然可以用 STL 算法在一個表達式之內搞定這些任務:for_each 輸出 key 和 value 的 Name(),而 find_if 找到 value 的 Name() 為 "Ralph" 的那一個元素,輸出是這樣的:

            123???? Amy
            234???? Ralph
            345???? Simon
            456???? Maggie
            Ralph's Id is: 234



            如果你想要把一個容器內的所有元素累加起來,應該怎么辦?

            如果你想要把一個容器內的所有元素累加起來,應該怎么辦?

            STL 的 accumulate 可以讓我們不必自己寫循環:

            #include <iostream>
            #include <functional>
            #include <numeric>
            #include <vector>
            #include <string>

            int main()
            {
            ? std::vector<int> vect;
            ? vect.push_back(1);
            ? vect.push_back(2);
            ? vect.push_back(3);
            ? vect.push_back(4);
            ?
            ? std::cout << "Accumulate: " <<
            ??? std::accumulate( vect.begin(), vect.end(), 0, std::plus<int>());
            }

            輸出:

            Accumulate: 10

            其中的 std::plus<int>() 可以省略,因為這將是3個參數的 accumulate 的默認行為。 注意 accumulate 算法是定義在 numeric 里面而不是 algorithm 里面的。

            由于 accumulate 和 plus 都是泛型的,所以如果你要累加的不是 int 而是字符串,對程序的修改也并不大:

            #include <iostream>
            #include <functional>
            #include <numeric>
            #include <vector>
            #include <string>

            int main()
            {
            ? std::vector<std::string> vect;
            ? vect.push_back("1");
            ? vect.push_back("2");
            ? vect.push_back("3");
            ? vect.push_back("4");
            ?
            ? std::cout << "Accumulate: " <<
            ??? std::accumulate( vect.begin(), vect.end(), std::string(""));
            }

            輸出:
            Accumulate: 1234

            不過,如果使用 boost.lambda ,這個問題會有一些很好看又容易理解的解法:

            #include <iostream>
            #include <algorithm>
            #include <numeric>
            #include <vector>
            #include <string>

            #include <boost/lambda/lambda.hpp>
            #include <boost/lambda/bind.hpp>
            //#include <boost/bind.hpp>

            using namespace boost::lambda;
            //using namespace boost;

            int main()
            {
            ? std::vector<std::string> vect;
            ? vect.push_back("1");
            ? vect.push_back("2");
            ? vect.push_back("3");
            ? vect.push_back("4");
            ?
            ? std::string result;
            ?
            ? std::for_each( vect.begin(), vect.end(), result += _1);
            ?
            ? std::cout << result;
            }

            輸出:
            1234

            這里要借用變量 result ,在這個程序中顯得多了幾行,但是我們調用 accumulate 的目的也往往是把結果放到一個變量中,這樣的話,使用 boost.lambda 反而會漂亮一些。

            在上面的程序中,另一個丑陋的地方就是 vector 的初始化,為了把 1, 2, 3, 4 放進 vect 里面,我們居然要調用 push_back 4次!不過,使用 boost.lambda 就好得多了。

            ? std::vector<int> vect(10);
            ? int i = 0;
            ? std::for_each( vect.begin(), vect.end(), _1 = ++var(i) );

            這里有兩個地方值得注意:
            1. 現在必須在 vect 的聲明中指出其大小,否則 for_each 對一個空容器可是什么也不會做
            2. 必須使用 ++var(i) ,而不是 ++i 。var 在這里的作用是強迫 lazy evaluation ,也就是讓變量在被用到的時候在求值,如果用 ++i ,你會得到一個裝有10個1的 vect ,而不是裝有1-10。

            =================================================================================

            許多問題遇到 map 都會變得復雜起來,如果想要把一個 map 中所有的 key 或者 value 累加起來,該怎么辦呢?這個時候已經不能直接使用 accumulate 了,用 boost.bind 可以辦到,做法是這樣的:

            #include <iostream>
            #include <algorithm>
            #include <numeric>
            #include <map>
            #include <string>

            #include <boost/bind.hpp>

            using namespace boost;

            int main()
            {
            ? std::map<int, std::string> persons;
            ? persons[123] = "Amy";
            ? persons[234] = "Ralph";
            ? persons[345] = "Simon";
            ? persons[456] = "Maggie";
            ?
            ? std::cout << std::accumulate( persons.begin(), persons.end(), 0,
            ??? bind(std::plus<int>(), _1, bind(&std::map<int, std::string>::value_type::first, _2)) )
            ??? << std::endl;

            ? std::cout << std::accumulate( persons.begin(), persons.end(), std::string(),
            ??? bind(std::plus<std::string>(), _1, bind(&std::map<int, std::string>::value_type::second, _2)) )
            ??? << std::endl;
            }

            輸出:

            1158
            AmyRalphSimonMaggie

            辦是辦到了,但是平心而論,的確算不上是漂亮。連續的 bind 并不比自己寫的循環更讓人頭暈。boost.lambda 也要用到 bind ,然而可以清晰許多:

            #include <iostream>
            #include <algorithm>
            #include <numeric>
            #include <map>
            #include <string>

            #include <boost/lambda/lambda.hpp>
            #include <boost/lambda/bind.hpp>

            using namespace boost::lambda;

            int main()
            {
            ? std::map<int, std::string> persons;
            ? persons[123] = "Amy";
            ? persons[234] = "Ralph";
            ? persons[345] = "Simon";
            ? persons[456] = "Maggie";

            ? int iresult = 0;
            ? std::string sresult;
            ?
            ? std::for_each( persons.begin(), persons.end(),
            ??? iresult += bind(&std::map<int, std::string>::value_type::first, _1)
            ? );
            ?
            ? std::for_each( persons.begin(), persons.end(),
            ??? sresult += bind(&std::map<int, std::string>::value_type::second, _1)
            ? );
            ?
            ? std::cout << iresult << std::endl;
            ? std::cout << sresult << std::endl;
            }

            輸出和上面的一樣:

            1158
            AmyRalphSimonMaggie

            有了它的幫助,即便間接層次再增加一層,也不會有太多困難:假如你的 map 并不直接存儲 string ,而是存儲 Person 對象,而它們的名字要通過 Name() 方法來取得,代碼只需要稍微的修改:


            #include <iostream>
            #include <algorithm>
            #include <numeric>
            #include <map>
            #include <string>

            #include <boost/lambda/lambda.hpp>
            #include <boost/lambda/bind.hpp>

            using namespace boost::lambda;

            class Person
            {
            public:
            ? Person(){}
            ? Person(const std::string& name) : name_(name){}
            ?
            ? std::string& Name()
            ? { return name_; }
            ?
            private:
            ? std::string name_;
            };

            int main()
            {
            ? std::map<int, Person> persons;
            ? persons[123] = Person("Amy");
            ? persons[234] = Person("Ralph");
            ? persons[345] = Person("Simon");
            ? persons[456] = Person("Maggie");

            ? std::string result;
            ?
            ? std::for_each( persons.begin(), persons.end(),
            ??? result += bind(&Person::Name, bind(&std::map<int, Person>::value_type::second, _1))
            ? );
            ?
            ? std::cout << result;
            }

            輸出:

            AmyRalphSimonMaggie






            上次提到過為容器生成數據的問題,我給出的用 boost.lambda 的方法是

            上次提到過為容器生成數據的問題,我給出的用 boost.lambda 的方法是:

            ? std::vector<int> vect(10);
            ? int i = 0;
            ? std::for_each( vect.begin(), vect.end(), _1 = ++var(i) );

            不錯,這樣可以生成連續的數字,也還算比較簡潔,因為代碼量不會隨著容器的大小而變化,不過,如果要在容器內填入隨機數呢?其實比上面更簡單,因為 STL 的 generate 算法就是設計來做這個的:

            ? std::vector<int> vect(10);
            ? std::generate(vect.begin(), vect.end(), rand);

            rand 是我們熟悉的標準 C 庫函數,這樣我們可以生成任意數量的隨機數了,不過還是有點不好的地方:每次生成的序列都是一樣的,因為 rand 生成的是偽隨機數。這個容易解決,我們必須先 seed 一下:

            ? std::vector<int> vect(10);
            ? srand(time(NULL));
            ? std::generate(vect.begin(), vect.end(), rand);

            好了,我們終于還是用了三行(其實是兩行,聲明 vector 總是必需的吧!),但是好歹是有了一個可用的方案。回頭看看,前面的連續整數問題也可以用 generate 來做,方法不言而喻:

            ? std::vector<int> vect(10);
            ? int i = 0;
            ? std::generate(vect.begin(), vect.end(), ++var(i));

            好處是 generate 本身更能說明這句話的用途,當然這個可能因人而異。

            我知道有人一定在問:一定要兩行么?一定要有一個初始變量么?答案是可以沒有,但是要用到另外的算法,再加上 boost.lambda 的協助。看看下面:

            ? std::vector<int> vect(10);
            ? std::partial_sum(vect.begin(), vect.end(), vect.begin(), _2 = _1 + 1);

            如果你現在把 vect 輸出,你會得到:

            0 1 2 3 4 5 6 7 8 9

            乍看起來不太好理解,我來慢慢解釋。
            partial_sum 的第4個參數是一個雙參數的 functor ,在這里,lambda 表達式 _2 = _1 + 1 充當了這個角色,它相當于

            f(x, y)? {? y? =? x? +? 1;? }

            而 partial_sum 呢?它把一個序列的 partial sum 送到結果序列中去,例如如果輸入一個數組 v[10] ,而輸出是 r[10] ,那么它的計算就是

            r[0] = v[0]????????????
            r[1] = f( r[0], r[1] )
            r[2] = f( r[1], r[2] )
            ......
            r[9] = f( r[8], r[9] )

            而當我們把 partial_sum 作用于 vect 本身,結果就成了

            vect[0] = vect[0]??????????????????????????? // vect[0] = 0
            vect[1] = (vect[1] = vect[0] + 1)?? // vect[1] = 1
            vect[2] = (vect[2] = vect[1] + 1)?? // vect[2] = 2
            ......
            vect[9] = (vect[9] = vect[8] + 1)?? // vect[9] = 9

            你一定發現其中的問題所在了:首先,我們必須依賴于編譯器把 vect[0] 初始化為0,其次,vect[0] = vect[0] 是不可回避的。以我當前所想到的,也只能這樣了。

            推廣一下,如果把
            _2 = _1 + 1 中的常數 1 換成另外的數字,我們就可以用一句話得到從 0 開始的等差數列,例如

            ? std::partial_sum(vect.begin(), vect.end(), vect.begin(), _2 = _1 + 3);

            得到的是

            0 3 6 9 12 15 18 21 24 27

            如果再發揮一點想象力,你就可以構造出更復雜的 lambda 表達式,從而得到更復雜的數組(也許這里叫數列更好吧),例如

            ? std::partial_sum(vect.begin(), vect.end(), vect.begin(), _2 = 2 * _1 + 1);

            得到的是 2 的 n 次方 - 1 數列

            0 1 3 7 15 31 63 127 255 511

            在 STL 算法中,adjacent_difference 和 partial_sum 是逆運算,因此,上面的事情也可以用 adjacent_difference 來做,只不過要把 lambda 表達式中的參數位置換一下,例如要得到 0, 3, 6... 的等差數列,只需要

            ? std::adjacent_difference(vect.begin(), vect.end(), vect.begin(), _1 = _2 + 3);

            而 2 的 n 次方 - 1 數列也是同樣道理

            ? std::adjacent_difference(vect.begin(), vect.end(), vect.begin(), _1 = 2*_2 + 1);

            如果你要生成倒序的數列呢?當然,STL 算法 reverse 可以派上用場,不過也不要忘了 STL 還有 reverse_iterator 這回事,用它就無需另外調用 reverse 了:

            ? std::partial_sum(vect.rbegin(), vect.rend(), vect.rbegin(), _2 = 2*_1 + 1);

            得到

            511 255 127 63 31 15 7 3 1 0

            最后還要提醒大家不要忘了一個很有用的 STL 算法: random_shuffle 。它可以把 Random access container 里面的值打亂,配合上面的數列生成,在很多場合是進行測試
            (例如測試排序算法) 的好工具。在我的機器上,下面兩行

            ? std::partial_sum(vect.begin(), vect.end(), vect.begin(), _2 = 2*_1 + 1);
            ? std::random_shuffle(vect.begin(), vect.end());

            得到打亂以后的數列:

            255 1 511 3 0 31 127 7 15 63

            =================================================================================

            有了強大的生成機制作基礎,下面的實驗也更加容易了。STL 的 count_if 和 find_if 都接受一個 predicate 作為比較的依據,而這個 predicate 往往非常簡單,以至于為它專門寫一個 functor 簡直不可接受。在第一篇里面已經展示了用 boost.lambda 生成臨時的無名 functor 的能力,這里再多說一點。

            下面先生成 2^n - 1 的數組,然后找出其中第一個大于100的數

            ? std::vector<int> vect(10);
            ? std::partial_sum(vect.begin(), vect.end(), vect.begin(), _2 = 2*_1 + 1);
            ?
            ? std::cout << *std::find_if(vect.begin(), vect.end(), _1 > 100);

            輸出為 127 ,如我們所料。同樣道理,如果是 count_if ,則會得到大于100的數的個數

            ? std::cout << std::count_if(vect.begin(), vect.end(), _1 > 100);

            輸出是 3 。注意細節:find_if 返回一個 iterator ,所以在它之前有 * 解引用,而 count_if 直接返回一個數字,無需解引用。

            與之類似的還有 STL 的 partition 算法,它根據傳入的 predicate 對一個序列進行劃分,predicate 得到 true 的將放在前面,其余的放在后面,返回的是那些“
            放在 后面”的元素中的第一個,換言之就是分界點。下面的代碼

            ? std::vector<int> vect(10);
            ? std::partial_sum(vect.begin(), vect.end(), vect.begin(), _2 = 2*_1 + 1);
            ?
            ? std::cout << *std::partition(vect.begin(), vect.end(), _1 > 100) << std::endl;
            ?
            ? std::for_each(vect.begin(), vect.end(), std::cout << _1 << " ");

            輸出為

            7
            511 255 127 7 15 31 63 3 1 0

            如果仔細觀察,還可以發現上面的輸出有點問題:數列中原有的順序(0, 1, 3, 7...)不復存在,這是因為 partition 并不是一個穩定排序的算法,它不保證排序結果保有原來的順序。如果需要穩定排序,可以使用 stable_partition 。只需要更改排序的那一句代碼為

            ? std::cout << *std::stable_partition(vect.begin(), vect.end(), _1 > 100) << std::endl;

            結果是

            0
            127 255 511 0 1 3 7 15 31 63

            當然,如果你還記得大學里的算法理論,就知道它們在效率上是有點區別的,partition 的復雜度保證為 O(n) ,具體地說是保證不超過 n/2 次交換;而 stable_partition 在最好情況下為 O(n) ,最差情況則達到 O(n*log(n)) 。

            順便說一下,上面的幾件簡單的事情,用標準的 STL 算法都可以辦到,只不過實在是……面目可憎:

            ? std::cout << *std::partition(vect.begin(), vect.end(),
            ??? std::bind2nd(std::greater<int>(), 100)) << std::endl;

            這句代碼做的事情和前面的 partition 一模一樣,但是孰優孰劣,大家自有公斷。


            總有一些時候,我們不能夠借助于“生成式”的初始化方法來給容器賦值,例如我們已經有了一個數組,要把它作為初值賦給一個容器,常規的做法已經深入人心了

            總有一些時候,我們不能夠借助于“生成式”的初始化方法來給容器賦值,例如我們已經有了一個數組,要把它作為初值賦給一個容器,常規的做法已經深入人心了:

            ? int init[] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
            ? std::vector<int> vect(init, init + sizeof(init)/sizeof(int));

            通過兩個 sizeof 來得到數組的大小在 C 語言里面是很常見的,然而在 C++ 里面,這即便不能稱為丑陋,也絕對稱不上是好。首先其可讀性不好,其次它要進行一次除法來得到一個本來在編譯期間就知道的數字,最后,它并不是總能用的!例如下面的例子:

            ? std::string strs[] = { "Amy", "Ralph", "Simon", "Maggie" };

            現在,你打算用 "sizeof " 什么來除以 "sizeof" 什么?

            其實,經過了這么多 C++ GP 的磨練,我們很容易就會想到一個在編譯期間得到靜態數組大小的辦法,模板偏特化是我們常用的武器,在這里非常好用:

            template <class T>
            struct ArraySize
            {
            ??? static const unsigned int value = 0;
            };

            template <class T, int S>
            struct ArraySize<T[S]>
            {
            ??? static const unsigned int value = S;
            };

            就這么簡單!雖然它只對付一維數組,但是擴展它是很容易的。不過,模板參數只能為類型,而我們需要傳入的是一個變量。好在在計算機科學里面,加一層抽象是可以解決任何問題的,我們只要加一個模板函數,C++ 會自動幫我們做類型推導:

            template <class T>
            unsigned int array_size(const T&)
            {
            ??? return ArraySize<T>::value;
            }

            現在我們可以輕而易舉的搞定那些數組了:

            ? int ints[] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
            ? std::vector<int> vint(ints, ints + array_size(ints));
            ?
            ? std::string strs[] = { "Amy", "Ralph", "Simon", "Maggie" };
            ? std::vector<std::string> vstr(strs, strs + array_size(strs));
            ?
            ? std::for_each(vint.begin(), vint.end(), std::cout << _1 << " ");
            ? std::cout << std::endl;
            ? std::for_each(vstr.begin(), vstr.end(), std::cout << _1 << " ");

            輸出:

            2 3 5 7 11 13 17 19 23
            Amy Ralph Simon Maggie

            順便說一下,在 boost.type_traits 里面有一個類似于 ArraySize 的工具,叫做 extent ,它更加強大,可以對付多維數組,不過是否值得為了這個而把 boost.type_traits 包含到工程里面去就看讀者自己抉擇了。

            =================================================================================

            容器的初始化是如此的常見,以至于 boost 提供了一個 assign 庫來簡化這些操作。boost.assign 大量利用了重載的逗號和括號來簡化賦值操作,提供了甚至比用數組更加簡潔的語法:

            #include <iostream>
            #include <algorithm>
            #include <vector>
            #include <string>

            #include <boost/assign/std/vector.hpp>
            #include <boost/assign/std/list.hpp>

            using namespace boost::assign;

            int main()
            {
            ? std::vector<int> vint;
            ? vint += 2,3,5,7,11,13,17,19,23;
            ?
            ? std::vector<std::string> vstr;
            ? vstr += "Amy","Ralph","Simon","Maggie";
            ?
            ? std::list<std::string> lstr;
            ? lstr += "Amy","Ralph","Simon","Maggie";
            ???
            ? std::for_each(vint.begin(), vint.end(), std::cout << _1 << " ");
            ? std::cout << std::endl;
            ? std::for_each(vstr.begin(), vstr.end(), std::cout << _1 << " ");
            ? std::cout << std::endl;
            ? std::for_each(lstr.begin(), lstr.end(), std::cout << _1 << " ");
            }


            運行這個程序,輸出與前面的大致相同,但是我們注意到初始化更加簡潔了,而且也不需要額外的空間來存儲數組,對于各種類型,都能夠以統一的方式來初始化,真是妙不可言。有趣的是 assign 的作者在文檔中還特意引用了 Bjarne Stroustrup 的話作為引子:

            There appear to be few practical uses of operator,().
            Bjarne Stroustrup, The Design and Evolution of C++

            這也許就是 C++ 最大的魅力之一:你無法預料它可以辦到些什么。

            下面關于 map 的例子也使用 boost.assign ,可以看到重載的括號給我們帶來了多少方便。

            #include <iostream>
            #include <algorithm>
            #include <map>
            #include <string>

            #include <boost/lambda/lambda.hpp>
            #include <boost/lambda/bind.hpp>

            #include <boost/assign/list_inserter.hpp>
            #include <boost/assign/list_of.hpp>

            using namespace std;
            using namespace boost::assign;
            using namespace boost::lambda;

            int main()
            {
            ? map<string,int> months;?
            ?
            ? insert( months )
            ??? ( "january",?? 31 )( "february", 28 )
            ??? ( "march",???? 31 )( "april",??? 30 )
            ??? ( "may",?????? 31 )( "june",???? 30 )
            ??? ( "july",????? 31 )( "august",?? 31 )
            ??? ( "september", 30 )( "october",? 31 )
            ??? ( "november",? 30 )( "december", 31 );
            ???
            ? map<int,string> persons = map_list_of
            ??? (2,"Amy")(3,"Ralph")
            ??? (5,"Simon")(7,"Maggie");
            ???
            ? for_each( months.begin(), months.end(),
            ??? cout << bind(&map<string,int>::value_type::second, _1) << "\t"
            ???????? << bind(&map<string,int>::value_type::first, _1) << "\n"
            ? );
            ? cout << endl;
            ? for_each( persons.begin(), persons.end(),
            ??? cout << bind(&map<int,string>::value_type::first, _1) << "\t"
            ???????? << bind(&map<int,string>::value_type::second, _1) << "\n"
            ? );?
            }

            輸出:

            30????? april
            31????? august
            31????? december
            28????? february
            31????? january
            31????? july
            30????? june
            31????? march
            31????? may
            30????? november
            31????? october
            30????? september

            2?????? Amy
            3?????? Ralph
            5?????? Simon
            7?????? Maggie


            posted on 2006-11-18 09:17 李錦俊(mybios) 閱讀(1625) 評論(2)  編輯 收藏 引用 所屬分類: C++

            Feedback

            # re: 【轉貼】泛型算法:Tips 2007-07-19 09:35 motioo
            不錯,不錯。謝謝。  回復  更多評論
              

            # re: 【轉貼】泛型算法:Tips 2008-10-15 20:21 ...
            非常經典的資源!十分感謝樓主!!!我直接整個網頁都下載了.  回復  更多評論
              

            亚洲AV无码久久精品成人 | 色综合合久久天天综合绕视看| 国色天香久久久久久久小说| 日韩精品久久无码中文字幕| 久久99国产精品尤物| 日本久久中文字幕| 久久夜色精品国产噜噜噜亚洲AV| 国内精品久久久久久野外| 亚洲人成电影网站久久| 久久人人爽人人爽人人片AV不| 久久99精品国产99久久6| 久久精品中文无码资源站| 91精品国产高清久久久久久国产嫩草| 久久久久国产一区二区| 久久精品国产清高在天天线| 亚洲精品无码久久久| 99久久久精品| 一本一本久久aa综合精品| 国产精品成人久久久久三级午夜电影| 伊人久久大香线蕉综合影院首页| 精品无码久久久久久国产| 久久免费视频网站| 久久久久久九九99精品| 国产精品99久久久精品无码| 免费一级欧美大片久久网| 久久免费精品一区二区| 国产精品青草久久久久婷婷 | 久久97久久97精品免视看| 粉嫩小泬无遮挡久久久久久| 国产香蕉久久精品综合网| 久久久久久av无码免费看大片| 久久精品国产免费| 国产精品久久免费| 品成人欧美大片久久国产欧美| 91久久精品91久久性色| AV狠狠色丁香婷婷综合久久| 久久亚洲日韩精品一区二区三区| 7777久久久国产精品消防器材| 日本WV一本一道久久香蕉| 99久久精品免费看国产一区二区三区| 久久青青草视频|