【问题标题】:Error: use of overloaded operator '[]' is ambiguous while building for i386错误:为 i386 构建时使用重载运算符“[]”不明确
【发布时间】:2018-09-11 10:49:49
【问题描述】:

考虑以下代码:

#include <stdio.h>
#include <stdint.h>

class test_class
{
public:
    test_class() {}
    ~test_class() {}

    const int32_t operator[](uint32_t index) const
    {
        return (int32_t)index;
    }

    operator const char *() const
    {
        return "Hello World";
    }
};

int main(void)
{
    test_class tmp;
    printf("%d\n", tmp[3]);
    return 0;
}

当我使用命令 clang++ -arch i386 test.cc 构建这些代码时,它会在 clang++ 上产生以下内容(Apple LLVM 版本 9.1.0 (clang-902.0.39.1)):

test.cc:24:21: error: use of overloaded operator '[]' is ambiguous (with operand types 'test_class' and 'int')
  printf("%d\n", tmp[3]);
                 ~~~^~
test.cc:10:17: note: candidate function
  const int32_t operator[](uint32_t index) const
                ^
test.cc:24:21: note: built-in candidate operator[](const char *, int)
  printf("%d\n", tmp[3]);
                    ^
test.cc:24:21: note: built-in candidate operator[](const volatile char *, int)

但是如果我只使用命令clang++ test.cc没有错误

i386 上的重载运算符 '[]' 似乎与 x86_64 上的不同,我想知道具体的区别是什么。

【问题讨论】:

  • 不知道为什么 2 个版本有差异。但是 i386 构建失败的原因是编译器可以使用用户提供的转换运算符然后索引 char 数组,或者只是索引变量。
  • @RichardCritten,要调用重载operator[](char *,int),第一个参数是由用户定义的转换构造的,因此它的等级应低于其他重载。我无法重现它,但它看起来像一个 clang 错误。
  • @Oliv 使用g++ -m32 test.cc 会产生“警告:ISO C++ 说这些是模棱两可的,即使第一个的最差转换优于第二个的最差转换”,以及 g++ 的版本是 (Ubuntu 7.2.0-8ubuntu3) 7.2.0 。但是 clang 认为这是一个错误。
  • 当一个类同时提供接受整数参数的operator[]() 以及转换为支持数组语法的任何类型(指针或类型为它自己的operator[]()。粗略的经验法则:要么提供operator[](),要么提供到指针类型的转换,而不是两者兼而有之。此外,无论如何解决歧义,printf("%d", tmp[3]) 都会给出未定义的行为,因为两者都没有可能性给出int 类型的结果。
  • @NinetyPercent - 因为在不同的构建中,int32_t 的大小可能相同,但像 int 这样的基本类型(包括文字 3)的大小不同。这会影响整数类型的排名(在您的情况下),进而会影响 operator[]() 函数的调用是否优于类型转换运算符。因此,适用于一种构建的东西可能不适用于另一种构建。这也是为什么最近的标准说这些东西是模棱两可的——因为实现之间存在差异[不同的构建相当于使用不同的实现,比如 32 位和 64 位编译器]。

标签: c++ operator-overloading x86-64 clang++ i386


【解决方案1】:

tmp[3] 有两种可能的解释:“显而易见”的一种,调用 test_class::operator[](int32_t),以及不太明显的一种,调用 test_class::operator const char*() 将对象转换为 const char*,并将索引应用于该对象指针。

为了决定使用哪个重载,编译器会查看所涉及的转换。每个重载都有两个参数:tmp3。对于第一次重载,tmp 不需要转换,但 3 必须从 int 转换为 int32_t。对于第二次重载,tmp 需要转换为const char*3 不必转换。

要选择适当的重载,编译器必须查看每个参数的转换集。对于第一个参数tmp,第一个重载不需要转换,第二个需要整数转换。所以第一个重载在这里获胜。对于第二个参数,第一个重载需要用户定义的转换,第二个不需要转换。所以第一次转换获胜。

简而言之:第一个重载在第一个参数上获胜,第二个重载在第二个参数上获胜。所以这个调用是模棱两可的。

您可以添加一个重载的operator[](int),这将解决这个特定的投诉,但如果int32_tint 的同义词,编译器就会出错。

您最好的选择可能是删除 operator[](int32_t) 并将其替换为 operator[](int)

这就是为什么您必须仔细考虑固定大小类型的原因:您可以获得意想不到的转化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-02
    • 2010-11-03
    • 1970-01-01
    • 1970-01-01
    • 2012-07-23
    相关资源
    最近更新 更多