【问题标题】:c++ error: operator []: 2 overloads have similar conversionsc++错误:运算符[]:2个重载有相似的转换
【发布时间】:2009-11-13 02:22:05
【问题描述】:
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;
}

【问题讨论】:

  • 问题是?
  • 这是一个众所周知的例子,即转换函数使事情出错。它还作为示例出现在“C++ 模板 - 完整指南”中。
  • 这就是为什么std::string 有一个data 和一个c_str 成员并且没有转换运算符。它避免了不必要的痛苦。

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


【解决方案1】:

如果您阅读了错误消息的其余部分(在输出窗口中),它会变得更加清晰:

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[]还是使用const T*上的内置operator[],它可以通过以下转换函数获得:

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

以下两个都是对违规行的潜在有效解释:

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

您可以通过将整数索引显式转换为无符号来消除歧义,这样就不需要转换:

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

或者通过将重载的operator[]s 更改为采用int 而不是unsigned int,或者删除operator T*() const(为了完整起见,也可能是非常量版本)。

【讨论】:

  • 您如何阅读该声明? "T 是一个指向不带参数的函数的常量指针,并且......"
  • 更准确地说,存在歧义的原因是因为例如文字1(用作索引)的类型是int,而不是unsigned int,因此需要转换才能调用重载程序operator[](unsigned int)。换句话说,有两个选择是v.operator float*()[0]v.operator[]((unsigned int)0)
  • @Shakedown:哪个声明?
  • 这是一个转换运算符,将 v3 转换为指向 const T 的指针。 operator() 将声明 const T* operator()();
  • 解释ptrdiff_t的作用或许也有用:错误信息通过显示一个裸露的int只说明了一半的真相——但它是ptrdiff_t,恰好是typedef 'ed 到该编译器上的int。在另一个系统上,ptrdiff_t 可能是long,此时您将面临内置op[] 和您自己的转换。在这种情况下,将采用您自己的参数,因为它与第一个参数(执行索引操作的对象 - *this)匹配得更好,并且与第二个参数匹配。
【解决方案2】:

简单来说:编译器不知道是将v转换为const float*然后使用operator[]作为指针,还是将0转换为unsigned int然后使用@987654326 @ 代表const v3

修复可能是删除operator[]。我想不出任何它给你的东西是 T* 的转换运算符还没有。如果您打算在operator[] 中进行一些边界检查,那么我会说用getPointer 函数替换转换运算符(因为通常您不想将安全的东西隐式转换为不安全的东西),或者做std::vector 所做的事情,即用户得到一个带有&amp;v[0] 的指针。

另一个可以编译的更改是更改operator[] 以采用int 参数而不是unsigned int。然后在您的代码中,编译器明确地选择没有转换的解释。根据我的编译器,即使使用无符号索引也没有歧义。哪个不错。

【讨论】:

    【解决方案3】:

    当你编译器编译以下内容时

    v[0]
    

    它必须考虑两种可能的解释

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

    没有一个候选者比另一个更好,因为每个候选者都需要转换。第一个变体需要用户定义的从v3&lt;T&gt;T* 的转换。第二个变体需要从int0int)到unsigned int 的标准转换,因为重载的[] 需要unsigned int 参数。这使得这些候选者无法比较(C++ 规则显然两者都不是更好),从而使调用不明确。

    如果你调用操作符为

    v[0U]
    

    歧义将消失(因为0U 已经是unsigned int)并且您的重载[] 将被选中。或者,您可以使用 int 参数声明重载的 []。或者您可以完全删除转换运算符。或者做其他事情来消除歧义 - 你决定。

    【讨论】:

    • @litb: P.S.正如我现在看到的那样,我应该使用 incomparable... :)))
    • @AndreyT,好吧,我读到“每个人都需要转换......这使得这些候选人无法比较”,因为这两种转换都同样糟糕。所以我想我指出它只是因为位置不同而模棱两可:不同的位置无法比较。
    【解决方案4】:

    罪魁祸首是您的类型转换运算符。 v 转换为浮点指针。现在有两种可能的操作符 [],一种是内置的 float 下标操作符,另一种是您在 v 上定义的操作符,语言应该选择哪一个,因此根据 ISO,这是一种歧义。

    【讨论】:

      【解决方案5】:

      记住一个类是它自己的朋友:

      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 的转换。另一种选择是使用 operator*() 来获取指向内部对象的指针,然后在指针上使用 [] 运算符。不允许编译器做出此选择并报错:

      编译器选项:

       _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)。

      【讨论】:

        【解决方案6】:

        直到 James McNellis 发布完整的错误消息,我才看到它,但两个 v3::operator[]() 函数之间的歧义并不像看起来那样。

        相反,由于参数类型之间没有完全匹配,编译器无法决定是否:

        a) 使用v3::operator[](unsigned int) const,从而将 int 参数转换为无符号,或

        b) 使用v3::operator const T*() const 转换,后跟内置数组索引运算符。

        您可以通过将 operator[] 参数设为整数而不是无符号整数来避免这种情况。但更好的解决方案是避免隐式转换为 T*,而是提供一个明确执行此操作的 Data() 函数。

        【讨论】:

          【解决方案7】:

          我遇到了同样的问题:我解决了这个问题,只是让 typecast 运算符显式。

          【讨论】:

            【解决方案8】:

            const 版本不修改任何内容。非const 版本允许您使用数组表示法 (v[3] = 0.5;) 分配事物。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-07-04
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-03-22
              相关资源
              最近更新 更多