這實際上是一個關於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*>::iterator
和T = 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
的比較中執行,其中左側獲得兩個額外const
:type const * const
。如果不清楚爲什麼這不會破壞const正確性,請發表評論,我會在這裏提供一些代碼。
*顯式模板實例化*與此完全無關,你的意思是*模板特化*? –
@DavidRodríguez-dribeas對於濫用條款感到抱歉。你是對的,我的意思是「部分模板專業化」。 – oyquan