2012-08-06 86 views
2

爲什麼使用非常量迭代器爲該行編譯以下代碼,但是對於常量迭代器(使用Visual Studio 2008)失敗?爲什麼只能在一種情況下推導出模板參數?

#include <vector> 

using std::vector; 

int main(int argc, char* argv[]) 
{ 
    vector<int> test; 

    test.push_back(1); 
    test.push_back(2); 

    vector<int>::const_iterator cit = test.end(); 
    std::distance(test.begin(), cit); // error: template parameter is ambiguous 

    vector<int>::iterator it = test.end(); 
    std::distance(test.begin(), it); 

    return 0; 
} 

注意:在Visual Studio 2008沒有向量成員函數cbegin()避免含糊,但一個重載begin()方法:

iterator begin() 
    { // return iterator for beginning of mutable sequence 
    return (iterator(_Myfirst, this)); 
    } 

const_iterator begin() const 
    { // return iterator for beginning of nonmutable sequence 
    return (const_iterator(_Myfirst, this)); 
    } 
+0

作爲一個經驗法則,你會發現,你需要使用'iterator'而不是'爲const_iterator '在大多數情況下 - 請參閱[有效STL](http://www.amazon.com/Effective-STL-Specific-Standard-Template/dp/0201749629)#26以獲取完整說明。 – 2012-08-06 08:50:21

回答

1

注:在Visual Studio 2008中沒有矢量成員函數cbegin()避免含混,但重載begin()方法:

我認爲編譯器總是爲非常量對象選擇非常量重載,而僅爲常量對象選擇const方法。

調用

std::distance(test.begin(), cit); 

瀏覽一下begin重載集合和cit類型,並找出它是否可以進行匹配。它首先解析過載(到非const的版本),因此失敗。

來表達你的意圖,也將編譯器的工作徹底的方法可能是:

vector<int> const &cref = test; 
vector<int>::const_iterator cit = cref.end(); 
std::distance(cref.begin(), cit); 
+0

你的調用示例應該是'std :: distance(test.begin(),cit) '來匹配我的例子,不是嗎?但很好的解釋,謝謝你的幫助。 – nabulke 2012-08-06 10:32:20

+0

我故意在const引用上調用'begin',強制選擇const過載 – Useless 2012-08-06 12:31:21

2

模板distance需要一個單一的參數,distance<T>(T first, T last) 。由於test.begin()的類型爲iteratorcit的類型爲const_iterator,因此無法推導出模板參數。

您可以使用test.cbegin()獲得有保證的const_iterator,或以其他方式說static_cast<std::vector<int> const &>(test).begin()

(這是一樣的,如果你有max<T>(T x, T y),並試圖說max(1, 2U) - 它不會編譯,因爲它是不明確的。)

+0

查看我的註釋:Visual Studio 2008矢量實現中沒有cbegin()。 – nabulke 2012-08-06 08:51:04

+0

我一直認爲添加/刪除常量保留給'const_cast',而不是'static_cast'。 – 2012-08-06 08:58:22

+1

@TadeuszKopec:只有**刪除** CV限定詞需要一個'const_cast'。添加常量是完全允許的轉換。 – 2012-08-06 10:20:38

0

函數模板std::distance只需要一個模板 參數,它必須是這兩個函數參數都是相同的。返回類型test.begin()的 只是iterator,而不是 const_iterator,所以函數參數的類型是不同的。 編譯器推導出第一個爲std::vector<int>::iterator,第二個爲 std::vector<int>::const_iterator,所以扣除 失敗。模板參數扣除不是考慮到 可能的轉換,但最簡單的轉換除外。

有很多方法可以解決事情,但它們都有明顯的缺陷 的缺點。 (這可能是爲什麼委員會增加cbegincend函數。)目前,你最好的選擇可能只是放棄const,並使用iterator

+0

我很困惑:begin()的返回類型是_either_ const_iterator _or_ iterator(請參閱問題中的註釋)。所以這兩個參數的類型不一定是不一樣的。 – nabulke 2012-08-06 08:55:51

+1

在const對象上調用的@nabulke'begin'返回'const_iterator'。在非const函數上返回'iterator'。 – 2012-08-06 09:03:16

+0

@TadeuszKopec:啊,謝謝你的評論!這是幫助我理解此問題的缺失鏈接。 – nabulke 2012-08-06 10:36:23

0

distance必須具有同一時間的模板參數。在這種情況下,它是_Vector_const_iterator。有兩個重載begin:const和非const。 const版本產生_Vector_const_iterator,但另一個產生_Vector_iterator_Vector_iterator繼承自_Vector_const_iterator

所以,const和的begin非const重載能夠產生_Vector_const_iterator的,他們都能夠被拾取用於非const對象,即編譯器被混淆。

這是我如何得到它來編譯:

std::distance(((const vector<int>&)test).begin(), cit); 
+0

但是爲什麼編譯器不會對_both_例子感到困惑呢? – nabulke 2012-08-06 09:02:50

+0

,因爲如果你使用第二個參數的非常量迭代器來計算距離,那麼編譯器只允許選擇非常量版本的begin() – 2012-08-06 09:04:08

相關問題