2012-01-26 61 views
12

我想寫一個模板函數,它與std::stack<T>東西和T一個實例,如:一個好的C++編譯器會優化一個引用嗎?

template<class StackType> inline 
bool some_func(StackType const &s, typename StackType::value_type const &v) { 
    // ... 
} 

我按引用傳遞v原因當然是優化的情況下StackType::value_typestructclass,而不是按值複製整個對象。

但是,如果StackType::value_type是像「int」這樣的「簡單」類型,那麼它當然更好。

現在的問題是:對於int這樣的類型,在上面的函數中會變成int const&作爲形式參數,編譯器是否會優化掉引用並僅通過值傳遞呢?

+2

這將是一個糟糕的編譯器。 – lapk

+0

如果編譯器實際上決定內聯函數,無論您如何定義它,它肯定會做最有效的事情。 – enobayram

回答

6

雖然我沒有真正測試過任何編譯器,但我對此表示懷疑。你假設傳遞一個const引用與傳遞一個值是無法區分的,但事實並非如此,編譯器不應該假設它是。

由於引用是const,因此函數不能通過它修改值,但代碼的其他部分可以訪問原始(非const)變量,並且可以修改它。例如,你的函數可能會調用其他一些函數來改變它,或者可能會有另一個線程同時運行。

如果其他內容修改了原始變量,則您的函數及其引用應該會看到新值。如果編譯器用一個副本替換引用,該函數仍然會看到舊值。

雖然你可能會對Boost的call traits庫感興趣。它提供了一個模板類型call_traits<T>::param_type,它是您不想複製的「大」類型的常量引用,以及「副」更有效的「小」類型的值。基本上,你希望編譯器隱含地做什麼,你可以明確地使用你的代碼。

+3

你對線程不正確。編譯器優化器經常會破壞代碼,這些代碼與糟糕的「無鎖」(天真)數據結構一起工作。編譯器需要考慮其他線程! –

+0

此外,簡單函數根本不會調用其他函數 –

+0

@AlexandrPriymak,該問題未指定函數中的代碼,因此它可能是調用其他函數的代碼。而且C++ 11包含一個併發感知內存模型和標準化的線程工具,所以C++ 11編譯器必須考慮其他線程可能做什麼,即使C++ 98編譯器不這樣做。 – Wyzard

6

只要編譯器能夠內聯,產生的跨程序優化將消除查找和傳遞地址的開銷。

對於非內聯函數調用,引用可能會作爲指針實現,而不是按值傳遞。

3

這是MSVC2010 64位的編譯器,全優化:

int foo(int i) 
{ 
int a = i + i; 

return (a); 
} 
//Disassembly: 
//00000001`3ff11c50 8d0409   lea  eax,[rcx+rcx] 
//00000001`3ff11c53 c3    ret 

int bar(int const & i) 
{ 
int a = i + i; 

return (a); 
} 
//Disassembly: 
//00000001`3ff11c10 8b01   mov  eax,dword ptr [rcx] 
//00000001`3ff11c12 03c0   add  eax,eax 
//00000001`3ff11c14 c3    ret 

在第一種情況,值被傳遞RCX,在第二 - 上RCX傳遞地址。當然,如果函數是內聯的,就像Ben說的那樣,那麼在兩種情況下生成的代碼可能都是完全相同的。

傳遞「小」類(例如,類只有一個int數據成員和普通的構造函數,按位拷貝構造函數等)的實例通過值更快。編譯器會將其優化爲基本上只傳遞int的副本。所以,你可以使用一些類型特徵來重載你的函數,就像Wyzard指出的那樣。類似的東西:

template< typename PType > 
PType Func(typename std::enable_if< (sizeof(PType) > sizeof(size_t)), PType const & >::type f) 
{ 
//large things: reference 
} 

template< typename PType > 
PType Func(typename std::enable_if< (sizeof(PType) <= sizeof(size_t)), PType >::type f) 
{ 
//small things: value 
} 
+1

請注意,您的實驗分析適用於「正常」功能,而不適用於模板化功能。這是一個關鍵的區別;普通函數不能(一般情況下)通過編譯器優化來調整其參數列表,而模板化函數必須是可以的並且因此可以。 –

+0

@BrooksMoses函數內聯不依賴於它是模板化的還是非模板化的函數。簡單地說,人們在每個編譯單元中使用函數定義的模板,並不能保證其內聯......如果我的「實驗」函數在一個編譯單元中同時聲明和定義,它們將被內聯。代碼將是相同的。反過來,即使是「簡單的」模板化函數也可能不會被內聯。 – lapk

+0

是的,我過於簡單化了。儘管如此,如果您的「實驗」函數是在單個編譯單元中聲明和定義的,但是聲明的方式是從該函數單元導出的,那麼它們不能被內聯。而且你沒有表明它們是靜態的,或者你正在編寫一個獨立的整體程序,這意味着它們必須被導出。 –

6

我期待在這裏gcc的優化選項http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

而且居然還有您的情況選擇:

-fipa-SRA

進行進程標更換集合,刪除未使用的參數以及通過按值傳遞的參數替換通過引用傳遞的參數。

啓用在級別-O2,-O3和-Os

據我所知,-02是在Linux發行版本常見的選項。

所以,簡單的答案是:編譯好的一個確實

相關問題