c ++错误:operator []:2个重载具有类似的转换

时间:2009-11-13 02:22:06

标签: c++ visual-c++ operator-overloading

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;
}

8 个答案:

答案 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*的转换。第二种变体需要从int0 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;)分配内容。