與臨時物件的鬥爭(下)
原載:www.cnblos.com/liyiwen 在
上篇裡,我們看到了 (n)rvo 和右值引用,下面我們來看看表示式模板。
如果有「系統地」學習過 c++ 的模板程式設計,那麼你應該已經知道 expression template 這個「東西」。在模板聖經《c++ templates》的第 18 章專門用了一整章來講這個技巧,(是的,我認為它是一種技巧)。足以見得它比較複雜,也很重要。
說起 expression template 產生,「臨時物件」也是「功臣」之一啊。還是來用例子來說明(你能很容易找到這樣類似的例子,呵,我就是參照著別人寫的):
view plain
copy to clipboard
?
classmyvec
myvec(myvecconst&a_left)
~myvec()
myvec&operator=(myvecconst&a_left)
return*this;
}int&operator(size_ta_idx)
intoperator(size_ta_idx)const
myvecconst
operator+(myvecconst&a_left)const
myvec&operator+=(myvecconst&a_left)
return*this;
}private:
static
intconstsize=100;
int*p;
};intmain(intargc,char*argv)
class myvec myvec(myvec const& a_left) ~myvec() myvec& operator=(myvec const& a_left) return *this; } int& operator (size_t a_idx) int operator (size_t a_idx)const myvec const operator + (myvec const& a_left) const myvec& operator += (myvec const& a_left) return *this; } private: static int const size = 100; int* p; }; int main(int argc, char* argv)
看,我們寫下這麼小段**:
view plaincopy to clipboard
? myvec d = a; d += b; d += c;
這是很常用的數**算吧,而且**很直觀。但這個表示式有乙個問題,就是產生了「不必要」的臨時物件。因為 a + b 的結果會成為乙個存在乙個臨時物件上 temp 上,然後這個 temp 再加上 c ,最後把結果傳給 d 進行初始化。如果這些向量很長,或是表示式再加幾節,可以想像這些 temp 會多讓人不爽。
而且,如果我們寫成這樣:
myvec d = a;
d += b;
d += c;
就可以避免產生多餘的臨時物件。但這樣寫,如果是不了解「**」的人看了myvec d = a + b + c;之後再看這段,是不是會覺得寫這**的人欠k?
好吧,你會問,上面不是說右值引用可以解決這樣問題?是的,但在沒有右值引用的「黑暗日子」裡,我們就不用過活了?當然要,小學開始數學老師就教我們要一題多解吧,換個思路也有辦法,這個辦法就是et。
怎麼做的呢?a + b + c 會產生臨時變數是因為 c++ 是即時求值的,在看到 a + b,就先算出乙個 temp 的vector物件,然後再向下算。如果能進行延遲求值,看完整個表示式再來計算,那麼就可以避免這個temp的產生。
怎麼做?
原來的做法中,operator + 直接進行了計算,既然我們不想它「過早」的計算,那麼我們就在重新過載乙個operator + 運算子,在這個運算中不進行真正的運算,只是生成乙個物件,在這個物件中把加法運算子兩邊的運算元保留下來~然後讓它參與到下一步的計算中去。(好吧,這個物件也是臨時的,但它的代價非常非常小,我們先不理會它)。於是我們寫下面的**:
view plain
copy to clipboard
?
class myvec; template class expplus int operator (size_t a_idx) const; }; // point 1 template expplusoperator + (l const& a_l, myvec const & a_r) class myvec myvec(myvec const& a_r) template myvec(exp const& a_r) } ~myvec() myvec& operator = (myvec const& a_r) return *this; } template myvec& operator = (exp const & a_r) return *this; } int& operator (size_t a_idx) int operator (size_t a_idx)const private: static int const size = 100; int* p; }; template int expplus::operator (size_t a_idx) const int main(int argc, char* argv)
比起之前的**來說,這段**有幾個重要的修改:首先,我們增加了乙個模板類 expplus,用它來代表加法計算的「表示式」,但在進行加法時,它本身並不進行真正的計算。對這個類,定義了下標運算子,這個運算子中才進行了真正的加法計算。然後,對於原來的 myvec,我們過載它的賦值運算子,讓它在賦值的時候通過expplus的下標運算子來獲得計算結果(也就是,在賦值操作時才真正的進行了計算!)。
上面這段話,對於不了解et的人來說,也許一時間還不容易明白,我們一步一步來:
在 d = a + b + c 這個式子中,首先遇到 a + b,這時,模板函式 operator + 會被呼叫(**中注釋了「point 1 」),這時只是生成乙個臨時的expplus物件(我們叫它 t1 吧),不做計算,只是保留計算的左右運算元(也就是a和b),接著,t1 + c ,再次呼叫同樣的 operator + ,而且也只是生成乙個物件(我們叫它 t2 吧),這個物件的型別是 expplus>,同樣,t2 在這裡只是保留了兩邊的運算元(也就是 t1 和 c)。直到整個表示式「做完」,沒有任何東西進行了計算,所做的事情實際上只是用 expplus 這個模板類把計算式的資訊記錄下來了(當然,這些資訊就是參與計算的運算元)。
最後,當進行 d = t2 的時候,myvec 的賦值運算子被呼叫(用 t2 作引數)。注意,這個呼叫中的語句 p[i] = t2[i],其中 t2[i] 通過過載的下標運算子,展開成 t1[i] + c[i],同理 t1[i] 又再次展開,成為 a[i]+b[i],最終,p[i] = t2[i] 就變成了:p[i] = a[i] + b[i] + c[i])(當然,裡面參雜了內聯的效果,這些函式都是非常容易被內聯的)。就像變「魔術」一樣,我們通過expplus完成了「延遲計算」,並避免了大型的 myvec 臨時物件的產生。
這基本上就是 et 的「原理」了吧。我們來「專門化」一下 et 的好處:
這樣,用 et 就能兼顧到「直觀」和「效率」了。
et 中 c++ 中的類庫里已經有非常多的應用了(包括 boost 中的多個子庫,以及 blitz++ 等高效能數學庫)
(n)rvo 是編譯器為我們做的優化手段,在能進行優化的情況下,nrvo 的表現是非常好的,因為它才真正的避免了臨時物件的產生(rvalue reference 和 expression template 中都可能還存在一些小型臨時物件),但 (n)rvo 有很多的限制條件。右值引用(rvalue reference )和 move 語意彌補了 (n)rvo 的不足之處,使得臨時物件的開銷最小化成為可能,但這也是有侷限的,比如,嗯,如果乙個類本身不動態地擁有資源……,那 move 就沒有意義了。expression template 保持了表示式直觀和效率兩者,很強大,但很顯然它太複雜,主要是作為類庫的設計者的**。另外,它也可能使得使用者要理解一些「新」東西,比如,如果我想儲存表示式的中間值,那麼 ...> 一定會讓我很頭大(不過有了 c++0x 的 auto 就好多了,呵呵)。
C 臨時物件(2)
與臨時物件的鬥爭 下 原載 www.cnblos.com liyiwen 在 上篇裡,我們看到了 n rvo 和右值引用,下面我們來看看表示式模板。如果有 系統地 學習過 c 的模板程式設計,那麼你應該已經知道 expression template 這個 東西 在模板聖經 c templates ...
C 臨時物件
臨時物件的產生 1.用建構函式作為隱式型別轉換函式時。2.建立乙個沒有名字的物件時。直接寫下 ctempobj 輸出 init obj exit obj 不單調用了建構函式,還呼叫了析構函式.既然是物件也可以這樣使用 ctempobj fun 不過這樣使用可要小心了 new ctempobj fun...
C 臨時物件
c 中有這樣一種物件 它在 中看不到,但是確實存在。它就是臨時物件 由編譯器定義的乙個沒有命名的非堆物件 non heap object 為什麼研究臨時物件?主要是為了提高程式的效能以及效率,因為臨時物件的構造與析構對系統效能而言絕不是微小的影響,所以我們應該去了解它們,知道它們如何造成,從而盡可能...