2015-12-02 49 views
1

考慮以下兩個代碼示例:本地引用int是否提供任何好處?

void int_fun(int val) {} 

void another_fun() { 
    vector<int> test(1, 1); 
    const int& int_ref = test[0]; 
    int_fun(int_ref); 
} 

​​

這是真的,編譯器可以實現int_ref如無操作,而在第二種情況下它會創建的測試[0副本]?或者複製也可以在第二種情況下進行優化,而這兩個樣本在性能上相當?

實例和問題是事實,有時代碼的清晰性是有益的建立,將馬上被傳遞給一些功能例如像在本地對象的動機。

是否使用基本類型(如int,布爾等)參考提供在這樣的情況下,或參考值和任何益處是等效(或可能值是更好)?

+1

你可以看一下彙編代碼。編譯器可能會刪除所有內容。 – juanchopanza

+0

爲什麼首先需要對變量進行額外引用?你可以寫'int_fun(test [0])'。 – user463035818

+0

@ tobi303你是對的,它只是爲了清晰起見 - 有時函數需要大量參數,每個參數的形式都是var.GetAnotherVar()。GetSomethingElse()。value(),所以它更容易分解很少 - 也許這只是個人喜好。 –

回答

2

在你給的情況下,我看不出有什麼真正的優勢。我懷疑大多數編譯器會在大多數情況下爲這兩個編碼生成相同的代碼(至少啓用了優化)。我認爲引用使得這個意圖更加清晰:真正的意圖是將原始對象傳遞給函數,並且無論出於何種原因,您都只是創建並傳遞別名到該對象。

在稍有不同的情況下,有可能是行爲上的真正的區別,但。最明顯的一個是,如果函數接收的參考:

void int_fun(int &val); 

void another_fun() { 
    vector<int> test(1, 1); 
    int &int_val = test[0]; 
    int_fun(int_val); 
} 

在這種情況下,int_fun可能修改它收到的參考價值,因此,如果您創建了一個基準,然後傳遞通過引用,引用將被摺疊,所以函數可以修改數組中的值(但是如果您創建了一個副本,然後通過引用傳遞它,副本將被修改而不是原來的)。

+0

謝謝。我用const更新了這個問題,以避免這種模糊性。 –

-1

使用當地的參考價值有時是有用的,即使是基本類型,如果他們在裏面更復雜的類型,這樣就可以使用該引用作爲一個讀/寫快捷方式到真正的變量。這裏的訣竅是部分。如果不需要修改變量,那麼複製該值也是一樣好。

舉例來說,一種更復雜代碼:

void fun() 
{ 
    vector<int> test; 
    //fill test with a lot of values 
    for (int i=0; i < test.size(); ++i) 
    { 
     int &x = test[i]; //<-- handy reference! 
     if (x > i) 
      ++x; 
     else if (x < i) 
      --x;     
    } 
} 

如果你想要寫同樣的邏輯,而不引用,你必須使用vector::operator[](int)至少兩次!

更改vectormap和東西更有意思:

void fun() 
{ 
    map<string, int> keys; 
    //fill keys 
    int &x = keys["foo"]; 
    ++x; 
} 
+0

這回答標題中的問題時,它的字面意思,但OP是要求可能的性能差異。 – user463035818

+0

@ tobi303:如果您注意到您正在將調用保存到外部的,可能很貴的函數(如'map :: operator []'),則性能差異很明顯。 – rodrigo

+0

您正在編寫自己的示例,在OP代碼中,既沒有地圖,也沒有兩個版本在調用「外部」函數時的不同。 – user463035818

0

代碼清晰。對於非平凡的代碼,有時使用對容器成員的引用會產生更清晰的代碼。另外,我可以在傳遞一個智能指針函數的時候這樣做。在函數內部,我將通過將它分配給一個引用來解引用它,並在整個函數中使用該引用。 這樣做會使它更清晰並避免複製對象。對於內置類型,我只是使用一個賦值並避免引用。

如果你真的需要的每一次表現,那麼你可以看看產生彙編語言代碼。之後,對其進行描述。

2

因爲在現代硬件上製作原始單個拷貝的代價非常低,所以您顯示的代碼示例是微型優化(或者微型優化,無論情況如何)。對基元進行引用也是一樣的。與調用任何感興趣的函數的成本相比,成本會降低。

但是,對示例的一個非常簡單的更改演示了在對基元的引用變得有益時的情況,因爲您可以將它們分配回去。

這裏就是你們的榜樣修改的地方使用std::map<std::string,int>std::vector<int>

std::map<std::string,int> cache; 

int compute_new(int old) { 
    return old+1; 
} 

void fun_with_ref(const std::string& key) { 
    int& int_ref = cache[key]; 
    int_ref = compute_new(int_ref); 
} 

void fun_with_val(const std::string& key) { 
    int int_val = cache[key]; 
    cache[key] = compute_new(int_val); 
} 

注意如何fun_with_ref執行由key單個查找,而fun_with_val需要兩個查找。 std::map<std::string,ing>的訪問時間增長爲O(日誌 N),因此當地圖增長到較大時節省可能變得很重要。

A quick micro-benchmark顯示使用具有1,000,000個條目的映射的引用的代碼幾乎是使用值的代碼的兩倍。

vector<string> keys; 
for (int i = 0 ; i != 1000000 ; i++) { 
    auto key = to_string(i); 
    cache[key] = i; 
    keys.push_back(key); 
} 
auto ms1 = duration_cast<milliseconds>(
    system_clock::now().time_since_epoch() 
).count(); 
for (const string& key : keys) { 
    fun_with_ref(key); 
} 
auto ms2 = duration_cast<milliseconds>(
    system_clock::now().time_since_epoch() 
).count(); 
for (const string& key : keys) { 
    fun_with_val(key); 
} 
auto ms3 = duration_cast<milliseconds>(
    system_clock::now().time_since_epoch() 
).count(); 
cout << "Ref: " << (ms2-ms1) << endl; 
cout << "Val: " << (ms3-ms2) << endl; 

輸出:

Ref: 557 
Val: 1064 
相關問題