2011-11-04 26 views
3

我在我的程序中使用QVector來保存指向對象的指針,例如FYPE *。爲什麼QVector <TYPE*> :: contains期望指向非const類型的指針作爲其參數?

class TYPE { 
     // .... 
    }; 
    const TYPE *ptrToCst; 
    QVector<TYPE*> qtVct; 
    // ... 
    if (qtVct.contains(ptrToCst)) { // Error!!! 
     // .... 
    } 

編譯器說QVector :: contains需要TYPE *而不是const TYPE *作爲它的參數。 const_cast操作可以解決這個問題。但它對我來說沒有任何意義,因爲contains方法不應該改變指針指向的內容。使用STL向量的等效代碼按預期工作。

std::vector<TYPE*> stlVct; 
    // ... 
    if (std::find(stlVct.begin(), stlVct.end(), ptrToCst)) { // Ok 
     // ... 
    } 

這種差異的原因是什麼? STL是否特意處理容納指針的容器,以便std :: find接受指向const對象的指針?我猜部分模板專業化涉及?

+0

*顯式模板實例化*與此完全無關,你的意思是*模板特化*? –

+0

@DavidRodríguez-dribeas對於濫用條款感到抱歉。你是對的,我的意思是「部分模板專業化」。 – oyquan

回答

2

這實際上是一個關於const正確性和一些非直觀錯誤的非常有趣的問題,但編譯器在拒絕代碼時是正確的。

爲什麼在Qt中不起作用?

編譯器拒絕代碼,因爲它會破壞代碼的常量正確性。我不知道該庫,但我想我可以放心地假設包含的簽名是QVector<T>::contains(T const & value)[1],這在T == type*的情況是指:

QVector<type *>::contains(type * const & value) 

現在的問題是,你是試圖通過類型爲type const *的變量。現在的問題是,編譯器不知道內部是什麼,只是它在界面中提供的承諾。 contains承諾不會更改指針,但沒有說明關於pointee。簽名中沒有任何內容阻止contains修改該值的實現。考慮這個類似的例子:

void f(int * const & p) { // I promise not to change p 
    *p = 5;     // ... but I can modify *p 
} 
int main() { 
    const int k = 10; 
    int const * p = &k; 
    f(p);     // Same problem as above: if allowed, f can modify k!!! 
} 

爲什麼它允許std::find那麼類似的呼籲?

std::find的區別在於find是一個模板,其中不同的參數類型具有非常鬆散的耦合。標準庫不在接口上執行類型檢查,而是在模板的實現上執行。如果參數不是正確的類型,它將被拾取,而實例化模板。

這意味着什麼是該實現將是類似於:

template <typename Iterator, typename T> 
Iterator find(Iterator start, Iterator end, T const & value) { 
    while (start != end && *start != value) 
     ++start; 
    return start; 
} 

類型的迭代器和值是完全無關的,沒有約束的存在比由內部的代碼強加其他模板。這意味着呼叫將匹配參數Iterator == std::vector<type*>::iteratorT = type const *,並且簽名將匹配。由於內部值僅用於與*start進行比較,並且type *type const * const的比較有效(它會將前者轉換爲後者,然後比較[2])代碼完美編譯。

如果模板有額外的限制,第二個參數的類型是Iterator::value_type const &(這可以使用SFINAE實現),那麼find將無法​​編譯時出現相同的錯誤。

[1]請注意聲明中訂購的選擇:type const *,而不是const type *。他們正是編譯器(和有經驗的眼睛),同樣通過總是加上const它可以輕鬆地將標識什麼被定義爲常量,並確定const T &T == type *,正確的在contains的論點是不是const type *&

[2]在,你不能一個type * const &綁定到type const *相同的方式,就可以這樣做的指針等效,type * const *不能從type const *如果const被添加到指針和兩個轉換指針類型,那麼轉換就很好,因爲它保證不會破壞const正確性。該轉換(這是安全的)在type * == type const * const的比較中執行,其中左側獲得兩個額外consttype const * const。如果不清楚爲什麼這不會破壞const正確性,請發表評論,我會在這裏提供一些代碼。

+0

對我的問題做出了傑出和徹底的回答! – oyquan

1

顯式模板實例化是針對具體類型完成的,您可以確定標準庫供應商不知道您會寫TYPE。除此之外,差別在於簽名。 std::find是一個免費的功能模板,是這樣的:

template <typename I, typename T> 
find(I first, I last, T value) 

所以,當你調用它,編譯器生成find(std::vector<TYPE*>::iterator, std::vector<TYPE*>::iterator, const TYPE*)。由於所有find都是比較,你可以比較const T*T*沒有問題,一切都很好,蓬鬆。

QVector<TYPE*>::contains另一方面,是類模板中的成員函數。因此,它的簽名包含用於實例化模板類型:

contains(TYPE*) 

,這其中就有問題了,因爲你嘗試用const TYPE*叫它 - 和const T*轉化爲T*是非法的。

另外:find返回一個迭代器,而不是一個布爾值。你的情況應該是if (std::find(...) != that_vector.end())

直接回答「爲什麼QVector :: contains期望指向非const類型的指針作爲它的參數」:因爲你已經告訴它,帶有模板參數。

+0

雖然你確實是對的,但我個人覺得這是Qt的嚴重缺點。這絕不是const正確的,當我嘗試在其周圍編寫const-correct代碼時,這會使我感到瘋狂。最後,我刪除了大部分'const'聲明,因爲它根本行不通。 – arne

+0

'find'模板通過參考引用值參數:'T const&',因爲可能是'QVector :: contains(T const&)'的情況(我不知道Qt,但我可以想象...) –

+0

@DavidRodríguez - dribeas:不相關的細節。 –

相關問題