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;
}
答案 0 :(得分: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; }
以下两项都是对违规行的潜在有效解释:
v.operator float*()[0]
v.operator[](0)
您可以通过显式地将整数索引转换为无符号来消除歧义,以便不需要转换:
_a[0] = v[static_cast<unsigned int>(0)];
或通过更改重载的operator[]
来取int
代替unsigned int
,或删除operator T*() const
(也可能是非const版本,为了完整性。)
答案 1 :(得分:5)
简单来说:编译器不知道是将v
转换为const float*
,然后使用operator[]
作为指针,还是将0
转换为{ {1}}然后将unsigned int
用于operator[]
。
修复可能是删除const v3
。我想不出它给你的任何东西,转换运算符到T *还没有。如果您打算在operator[]
中进行一些边界检查,那么我会说使用operator[]
函数替换转换运算符(因为通常您不希望隐式地将安全事件转换为不安全的事情),或做getPointer
做的事情,即用户得到std::vector
的指针。
允许编译的另一个变化是更改&v[0]
以取operator[]
参数而不是int
。然后在您的代码中,编译器明确地选择没有转换的解释。根据我的编译器,即使使用无符号索引,仍然没有歧义。哪个好。
答案 2 :(得分:3)
当编译器编译以下
时v[0]
必须考虑两种可能的解释
v.operator T*()[0] // built-in []
v.operator[](0) // overloaded []
两个候选人都不比另一个好,因为每个人都需要转换。第一个变体需要用户定义的从v3<T>
到T*
的转换。第二种变体需要从int
(0
int
)到unsigned int
的标准转换,因为您的重载[]
需要unsigned int
参数。这使得这些候选人无法比拟(C ++规则显然不会更好),从而使得这个呼叫变得更加灵活。
如果您调用运算符
v[0U]
歧义将消失(因为0U
已经是unsigned int
),您的重载[]
将被选中。或者,您可以使用[]
参数声明重载的int
。或者您可以完全删除转换运算符。或者做一些其他事情来消除歧义 - 你决定。
答案 3 :(得分:2)
这是你的类型转换运算符的罪魁祸首。 v转换为浮点指针。现在有两个operator []可能,一个是float的内置下标运算符,另一个是你在v上定义的那个,哪个应该是语言选择,所以根据ISO它是一个歧义。
答案 4 :(得分:2)
记住课程本身就是朋友:
v3(const v3<T> & v)
{
_a[0] = v._a[0];
_a[1] = v._a[1];
_a[2] = v._a[2];
}
复制相同类型的内容时,您已经了解了实现细节。因此,如果合适,直接访问实现不是问题。因此,从构造函数中,您可以直接访问要复制的对象,并查看其成员“_a”。
如果您想了解原始问题:
上下文'v [1]'中的文字'1'是一个整数(这是有符号整数的同义词)。因此,要使用operator [],编译器在技术上需要插入从int到unisgned的转换。另一种方法是使用运算符*()来获取指向内部对象的指针,然后在指针上使用[]运算符。编译器不允许做出此选择并出错:
编译器选项:
_a[1] = v[1];
// Options 1:
_a[1] = v.operator[]((unsigned int)1);
// Options 2:
_a[1] = v.operator*()[1];
为了使它不受欢迎,你可以使用无符号文字;
_a[1] = v[1u];
从长远来看,用户可能更容易这样做 将operator []转换为使用int而不是unsigned int,然后在整数文字时获得完全匹配(或者你可以有两组operator []。一个使用int,一个使用unsigned int)。
答案 5 :(得分:1)
我没有看到它,直到James McNellis发布了完整的错误消息,但模糊性不是两个v3::operator[]()
函数之间的模糊性。
相反,由于参数类型之间没有完全匹配,编译器无法决定是否:
a)使用v3::operator[](unsigned int) const
,从而将int参数转换为unsigned或
b)使用v3::operator const T*() const
转换,然后使用内置数组索引操作符。
你可以通过使operator []参数为int而不是unsigned int来避免这种情况。但更好的解决方案是避免隐式转换为T *,而是提供明确执行此操作的Data()函数。
答案 6 :(得分:0)
我遇到了同样的问题:我只是简单地将类型转换操作符解析了。
答案 7 :(得分:-2)
const
版本不会修改任何内容。非const
版本允许您使用数组表示法(v[3] = 0.5;
)分配内容。