2011-07-24 75 views
4

注:爲了澄清,問題不在於對一般使用restrict關鍵字,但特別地約其施加到構件用作描述here限制上成員函數限定符(限制此指針)

GCC允許您使用的__restrict__(GNU的++相當於C99的restrict)限定符上的成員函數,有效地使this一個限制的功能的範圍內合格指針。牛肉在哪裏?

大多數成員函數都在其他成員上工作,通過this訪問它們,這是一個T* const(通常是unaliased)。對於this可能是別名,需要在成員函數中使用第二個指向類型的指針,並且它必須來自某處。
這就是非成員函數的情況,比如所有的二元運算符或者至少需要兩個指針或引用相同的非平凡類型的任何其他自由函數。但是,這些功能沒有this,所以它們不相關。

賦值運算符,拷貝構造,和一元比較運算符其中this可以原則上被混疊(因爲另一個目的通過引用傳遞)的成員函數的實例。因此,爲這些分配一個限制限定符纔有意義 - 編譯器應該已經很清楚所有其他函數都具有restrict屬性(因爲從來沒有第二個指針指向T)。現在

,例如,如果您使用的restrictoperator=你應該必然不檢查自賦值可言,因爲你說this沒有該功能(和的範圍內,別名,如果這是真的,不可能發生自我分配)。
顯然,這是你事先不可能知道的事情,也是沒有道理的。

那麼,如果真的想給成員函數一個限定符限定符並且它有意義,那麼會出現什麼情況呢?

+0

我認爲這樣做沒有任何意義。你會用'this'進行去鋸齒 - 它不是一個真正的連續的相同類型的數組嗎? – Anycorn

+0

@Anycom:如果這個類有數據成員,任何兼容的指針都可以是一個數據成員的別名。 – MSalters

回答

3

要麼我失去了一些東西,或者你的問題沒有意義。 this從任何其他參數的成員函數不同,那你爲什麼驚訝的是GCC,可以應用restrict呢?

關於它應用到賦值運算符,你正確地指出,這將消除對一個明確的自我分配測試的需要。然後你說:

顯然,這是東西,你不可能提前知道

但是當你使用restrict任何東西,這是總是如此。例如,有人可能會決定調用memcpy與重疊的內存區域;你「不可能事先知道」他們不會這樣做。但memcpy的參數的restrict聲明意味着如果他們的確做了,他們已經犯了錯誤。完全一樣,如果你聲明瞭一個賦值操作符restrict,那麼你就錯誤地讓某人自己分配了該類的對象。這一點根本沒有什麼神祕或矛盾的;它只是restrict的語義的一部分,它對代碼的其餘部分施加了一定的限制。

我也不清楚爲什麼你會發現它如此不可能的成員函數採取指針(或引用)到同一類型的另一個對象。微不足道的例子:

class Point { 
public: 
    double distance(const Point &other) const; 
}; 

這種事情總是出現。

所以,真正的問題是,爲什麼你覺得this是任何其他的說法有很大不同?或者,如果你願意,我怎麼完全錯過了你的觀點?

0

恐怕我不明白你爲什麼在談論this

restrict指定指針不與其他指針重疊。因此,編譯器可以假定限制指針指向的內存區域不依賴,這允許更積極的優化。當用於其他指針變量時,__restrict__會比this更有效。

那麼,如果真的想給 成員一個限定符限定符並且它有意義,那麼會出現什麼情況呢?

回想起剛纔在memcpy使用restrict指針的典型案例:

void Foo::MyCompute(__restrict__ char* bufA, __restrict__ char* BufB) 
{ 
} 
+0

查看最後兩段[here](http://gcc.gnu.org/onlinedocs/gcc/Restricted-Pointers.html#Restricted-Pointers)。我知道在函數參數或塊內使用'restrict',這當然是完全清楚的。我的問題是關於給予'this'限制條件(如鏈接文檔中所述),這對我來說似乎沒有任何意義。 – Damon

1

您發佈的鏈接是有趣的。在this上應用restrict將不會有堅實的用例。正如你在你的問題中提到的,拷貝構造函數,operator =可能是潛在的候選者;但編譯器可以照顧他們。

但下列情況下可以是有趣

struct A 
{ 
    //... 
    void Destroy (A*& p) __restrict__ 
    { 
    delete this; 
    p = 0; 
    p++; 
    } 
}; 

現在使用的情況下即可;

A **pp = new A*[10]; 
for(int i = 0; i < 10; i++) 
    pp[i] = new A; 
//... 
A* p = pp[0]; 
for(int i = 0; i < 10; i++) 
    p->Destroy(p); 
delete[] pp; 

雖然這是非常不尋常的做法,但這是我唯一能想到的用例。

+0

嘿,有趣的例子,雖然我認爲這甚至不是「不尋常」的,但是有些人會想要避免,因爲它們有點不明顯,並且有奇怪的語義。語義有點像「給定任何_different_指向對象,我聲明它不是我的對象,無論如何銷燬_my object_,並修改指向其他對象的指針」。 – Damon

+0

@Damon,這是正確的;但是編譯器在明顯和非明顯之間可能沒有區別。無論如何,限制'this'指針對我來說沒有多大意義。讓我們看看是否有人有更好的答案。 – iammilind

0

添加此作爲答案,因爲它可能更適合這樣(這是一種答案,並不真正屬於這個問題,再加上它有點評論)。

想着Nemo的答案很長一段時間之後,我相信,我們對自轉讓雙方的解釋是,也許有些錯誤的(雖然Nemo的比我更正確)。正如Nemo正確指出的那樣,具有限制條件的this實際上意味着存在別名是程序錯誤。沒有更多,不少。當你寫這篇文章時,你的邏輯實際上不應該是「因爲你說這不可能發生,所以你不應該檢查自賦值」,而是「因爲你明確地說別名不可能發生,而且它是一個程序錯誤,如果是的話,你不僅需要檢查自賦值,但你必然必須發生故障的硬盤,如果它發生。」

而且,它強調了一個特定的程序邏輯,同時允許編譯器針對該特殊情況更好地進行優化,因此的確有意義

2

我相信你們失蹤的是一個成員函數的參數也可以別名部分或一個對象。這裏有一個例子

struct some_class { 
    int some_value; 

    void compute_something(int& result) { 
     result = 2*some_value; 
     ... 
     result -= some_value; 
    } 
} 

一個可能會想到編譯到

*(this + offsetof(some_value)) -> register1 
2*register1 -> register2 
... 
register2 - register1 -> result 

該代碼,不幸的是,將錯誤如果有人傳遞一個參考SOME_VALUE的結果。因此,編譯器實際上需要生成以下代碼

*(this + offsetof(some_value)) -> register1 
2*register1 -> register2 
register2 -> result 

... 
*(this + offsetof(some_value)) -> register1 
result -> register2 
register2 - register1 -> register2 
register2 -> result 

這顯然效率較低。需要注意的是,除非compute_something是內聯,編譯器知道是否結果可能別名SOME_VALUE與否沒有方式,所以它具有假設最壞的情況下,無論是任何聰明還是愚蠢它。所以即使應用於這個指針,也有一個確定的,非常真實的限制優勢。