【问题标题】:operator[](const char *) ambiguityoperator[](const char *) 歧义
【发布时间】:2014-08-04 07:27:35
【问题描述】:

以下代码

#include <string>

struct Foo {
    operator double() {
        return 1;
    }

    int operator[](std::string x) {
        return 1;
    }
};

int main() {
    Foo()["abcd"];
}

使用 g++ 可以正常编译,但使用 clang 和 intel 编译器会失败,因为声明的方法和本机运算符 [] 之间存在歧义。

如果Foo 隐式转换为int,我会很清楚,但这里转换为double。这不是解决歧义吗?

【问题讨论】:

  • 如果您将转换运算符删除为double,会发生什么情况?
  • 在没有转换运算符的情况下可以在 clang 上正常工作。
  • 这和stackoverflow.com/questions/8914986/…的问题差不多
  • 当你删除Foo::operator[]时它是否与icc一起编译?
  • @n.m.在我的计算机上使用 icl(icc 的 Windows 版本)进行了检查,它确实可以编译。但随后 icl 也错误地编译了 §13.3.1.2 [over.match.oper]/p7 中标准规定不应编译的示例之一,所以...

标签: c++


【解决方案1】:

§13.3.3.1.2 [over.ics.user]/p1-2:

用户定义的转换序列由初始标准组成 转换序列后跟用户定义的转换 (12.3) 其次是第二个标准转换序列。如果用户定义 转换由构造函数 (12.3.1) 指定,初始 标准转换序列将源类型转换为类型 由构造函数的参数要求。如果用户定义 转换由转换函数 (12.3.2) 指定,初始 标准转换序列将源类型转换为隐式 转换函数的对象参数。

第二个标准转换序列转换结果 用户定义转换为序列的目标类型。

特别是,存在从浮点到整数类型的隐式转换(§4.9 [conv.fpint]/p1):

浮点类型的纯右值可以转换为 整数类型。转换截断;也就是小数部分 被丢弃。如果截断的值不能,则行为未定义 以目标类型表示。

出于重载解决的目的,适用的候选者是:

Foo::operator[](std::string x)              // overload
operator[](std::ptrdiff_t, const char *);   // built-in

给定类型(Foo, const char [5])的参数列表。

要匹配第一个运算符函数,第一个参数是精确匹配;第二个需要用户定义的转换。

要匹配第二个内置函数,第一个参数需要用户定义的转换序列(用户定义的转换为double,然后是标准转换为std::ptrdiff_t,浮点整数转换)。第二个参数需要标准的数组到指针的转换(仍然是精确匹配等级),这比用户定义的转换要好。

因此对于第一个参数,第一个函数更好;对于第二个参数,第二个函数更好,我们有一个纵横交错的情况,重载决议失败,程序格式错误。

请注意,虽然出于运算符重载决议的目的,用户定义的转换序列可以有两个标准转换序列(一个在用户定义的转换之前,一个在用户定义的转换之后),非类类型的操作数可以是转换为匹配候选,如果选择了内置运算符,则第二个标准转换序列不适用于类类型的操作数,并且在运算符被解释为之前,对非类类型的操作数根本不应用任何转换内置(§13.3.1.2 [over.match.oper]/p7):

如果通过重载决议选择了内置候选,则 类类型的操作数转换为对应的类型 所选操作功能的参数,除了第二个 用户定义转换序列的标准转换序列 (13.3.3.1.2) 不适用。然后将操作员视为 对应的内置运算符并根据第 5 条进行解释。

因此,如果 Foo::operator[](std::string x) 被删除,编译器应该会报告错误,尽管 clang 不会。这是一个明显的clang错误,如it fails to reject the example given in the standard

【讨论】:

  • 然而 clang 拒绝 1.0["foo"] 为无效。
  • @n.m.这也是正确的 - 在这种情况下没有重载解决方案。此外,如果重载决议选择了一个内置的,结果不一定有效。
  • 在我的机器上,clang 抱怨 Foo::operator[](std::string)内置 operator[](int, const char *) 之间的歧义(不涉及 ptrdiff_t)。当您删除 Foo::operator[] 时,它会愉快地选择一个没有错误的内置。但是内置函数要求参数之一是整数类型。你怎么解释这个?
  • @n.m. std::ptrdiff_t 是有符号整数类型的 typedef(显然在您的机器上它是 int - 我假设它是 32 位的?)。第二个是clang bug。它不应该转换非类类型的参数或对类类型的参数应用第二个标准转换序列,但它确实如此。
  • 有两种可能的解释。 (1) 内置不可行,因此应该从重载集中删除它,并选择Foo::operator[] 作为唯一剩余的元素。由于 clang 错误地认为内置是可行的,这不会发生。 (2) 标准暗示内置是可行的,并要求在重载解决阶段之后 拒绝它。如果是这种情况,那就是标准中的缺陷(意外且不合逻辑的行为,没有任何保证)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-19
  • 2013-11-14
  • 2012-04-19
  • 1970-01-01
相关资源
最近更新 更多