2009-11-13 51 views
3
template <typename T> 
class v3 { 
private: 
    T _a[3]; 

public: 
    T & operator [] (unsigned int i) { return _a[i]; } 
    const T & operator [] (unsigned int i) const { return _a[i]; } 

    operator T *() { return _a; } 
    operator const T *() const { return _a; } 

    v3() { 
     _a[0] = 0; // works 
     _a[1] = 0; 
     _a[2] = 0; 
    } 

    v3(const v3<T> & v) { 
     _a[0] = v[0]; // Error 1 error C2666: 'v3<T>::operator []' : 2 overloads have similar conversions 
     _a[1] = v[1]; // Error 2 error C2666: 'v3<T>::operator []' : 2 overloads have similar conversions 
     _a[2] = v[2]; // Error 3 error C2666: 'v3<T>::operator []' : 2 overloads have similar conversions 
    } 
}; 

int main(int argc, char ** argv) 
{ 
    v3<float> v1; 
    v3<float> v2(v1); 

    return 0; 
} 
+6

問題是? – Tom 2009-11-13 02:25:26

+0

這是一個衆所周知的轉換函數使事情出錯的例子。它也出現在「C++模板 - 完整指南」中。 – 2009-11-13 06:44:21

+0

這就是爲什麼'std :: string'有'data'和'c_str'成員並且沒有轉換操作符的原因。它避免了不必要的痛苦。 – 2009-11-13 10:35:17

回答

12

如果你讀了錯誤消息的其餘部分(在輸出窗口),它成爲一個更清楚一點:

1>  could be 'const float &v3<T>::operator [](unsigned int) const' 
1>  with 
1>  [ 
1>   T=float 
1>  ] 
1>  or  'built-in C++ operator[(const float *, int)' 
1>  while trying to match the argument list '(const v3<T>, int)' 
1>  with 
1>  [ 
1>   T=float 
1>  ] 

編譯器不能決定是否使用過載operator[]或內置operator[]const T*,它可以由下面的變換功能獲得:

operator const T *() const { return _a; } 

兩個違規線的下面是潛在有效解釋:

通過改變你的超載 operator[] s到採取 int,而不是 unsigned int

_a[0] = v[static_cast<unsigned int>(0)]; 

,或者:

v.operator float*()[0] 
v.operator[](0) 

您可以通過顯式鑄造整數索引,這樣,不需要轉換到無符號消除不確定性或者通過刪除operator T*() const(也可能是非const版本,爲了完整性)。

+0

你如何閱讀該聲明? 「T是指向函數的常量指針,它不帶任何參數和...」 – 2009-11-13 02:39:21

+0

更確切地說,造成歧義的原因是,例如,文字'1'(用作索引)的類型是'int',而不是'unsigned int',所以它需要轉換來調用overloader'operator [](unsigned int)'。換句話說,兩種選擇是'v.operator float *()[0]'和'v.operator []((unsigned int)0)'。 – 2009-11-13 02:41:46

+0

@Shakedown:哪個聲明? – 2009-11-13 02:42:36

-2

const版本不會修改任何內容。非const版本允許您使用數組表示法分配事物(v[3] = 0.5;)。

5

簡單來說:編譯器不知道是否v轉換爲const float*然後用operator[]爲指針,或將0轉換爲unsigned int,然後使用operator[]const v3

修復可能是要刪除operator[]。我想不出任何它給你的轉換操作符到T *還沒有。如果您打算在operator[]中進行一些邊界檢查,那麼我會說用getPointer函數替換轉換運算符(因爲通常您不希望將安全的事情隱式轉換爲不安全的東西),或者做什麼std::vector確實,這是用戶得到一個指針&v[0]

讓它編譯的另一個更改是將operator[]更改爲int參數而不是unsigned int。然後在你的代碼中,編譯器毫不含糊地選擇沒有轉換的解釋。根據我的編譯器,即使使用無符號索引,仍然沒有歧義。這很好。

2

這是你的類型轉換運算符是罪魁禍首。 v轉換爲浮點型指針。現在有兩個運算符[],一個是內置的下標運算符,另一個是你在v上定義的運算符,哪一個是語言選擇的,所以它是根據ISO的模糊性。

2

記住一類是自身的朋友:

v3(const v3<T> & v) 
{ 
    _a[0] = v._a[0]; 
    _a[1] = v._a[1]; 
    _a[2] = v._a[2]; 
} 

當複製的東西你已經暴露在實施細則相同類型的。因此,如果適當,直接訪問實施並不是問題。因此,從構造函數中,您可以直接訪問要複製的對象並查看其成員'_a'。

如果你想知道最初的問題:

在上下文中的文字「1」,「V [1]」是一個整數(這是有符號整數的代名詞)。因此要使用操作符[],編譯器在技術上需要插入從int到unisgned的轉換。另一種方法是使用運算符*()來獲取指向內部對象的指針,然後在指針上使用[]運算符。編譯器是不允許做出這樣的選擇和錯誤的:

編譯器選項:

_a[1] = v[1]; 
// Options 1: 
_a[1] = v.operator[]((unsigned int)1); 
// Options 2: 
_a[1] = v.operator*()[1]; 

爲了讓unabigious你可以使用一個無符號的文字;

_a[1] = v[1u]; 

從長遠來看,它可能是值得使用戶更容易。
將運算符[]轉換爲使用int而不是unsigned int,那麼當整數文字(或者可以有兩組運算符[]。一個使用int,另一個使用unsigned int)時,您將得到完全匹配。

3

當你的編譯器編譯以下

v[0] 

它必須考慮兩個可能的解釋

v.operator T*()[0] // built-in [] 
v.operator[](0) // overloaded [] 

兩位候選人比其他更好的,因爲每一個需要一個轉換。第一個變體需要用戶自定義的從v3<T>T*的轉換。第二個變體需要從int0int)到unsigned int的標準轉換,因爲您的超載[]需要unsigned int參數。這使得這些候選人無法比擬(由C++規則來看,這兩者都顯然更好),並因此使呼叫變得非常具有說服力。

如果調用運營商爲

v[0U] 

的模糊性將消失(因爲0U已經是一個unsigned int)和您超載[]將被選中。或者,您可以用int參數聲明您的超載[]。或者你可以完全刪除轉換操作符。或者做一些其他的事情來消除模糊 - 你決定。

+0

@litb:P.S.我應該使用*無與倫比的*,因爲我現在看到... :))) – AnT 2009-11-13 17:23:57

+0

@AndreyT,以及我讀到「每個人都需要轉換......這使得這些候選人無法比擬」,因爲兩個轉換同樣不好。所以我想我指出,由於不同的立場,它只是模棱兩可:不同的立場是無法比擬的。 – 2009-11-14 02:49:01

1

我沒有看到它,直到James McNellis發佈了完整的錯誤消息,但模糊不在兩個v3::operator[]()函數之間,因爲它似乎是。

相反,由於參數類型之間沒有精確匹配,編譯器無法決定是否要:

a)使用v3::operator[](unsigned int) const,從而int參數轉換爲無符號,或

B)使用v3::operator const T*() const轉換後跟內置的數組索引操作符。

你可以通過使用operator []參數int而不是unsigned int來避免這種情況。但更好的解決方案是避免對T *的隱式轉換,而是提供一個明確做到的Data()函數。

0

我有這個相同的問題:我解決它只是簡單地使類型轉換運算符顯式。