【问题标题】:Use of overloaded operator '[]' is ambiguous with template cast operator重载运算符 '[]' 的使用与模板转换运算符不明确
【发布时间】:2019-02-08 11:32:36
【问题描述】:

以下代码在 gcc 7.3.0 中编译良好,但不能在 clang 6.0.0 中编译。

#include <string>

struct X {
    X() : x(10) {}
    int operator[](std::string str) { return x + str[0]; }
    template <typename T> operator T() { return x; } // (1) fails only in clang
    //operator int() { return x; } // (2) fails both in gcc and clang
private:
    int x;
};

int main() {
    X x;
    int y = 20;
    int z = int(x);
    return x["abc"];
}

我使用命令clang++ 1.cpp -std=c++98 指定不同的标准版本。我试过 c++98,11,14,17,2a。在所有情况下,错误都是相同的。 clang 中的错误信息如下:

1.cpp:14:13: error: use of overloaded operator '[]' is ambiguous (with operand types 'X' and 'const char [4]')
    return x["abc"];
           ~^~~~~~
1.cpp:5:9: note: candidate function
    int operator[](std::string str) { return x + str[0]; }
        ^
1.cpp:14:13: note: built-in candidate operator[](long, const char *)
    return x["abc"];
            ^
1.cpp:14:13: note: built-in candidate operator[](long, const volatile char *)
1 error generated.

什么编译器在这种情况下正确地遵循标准?它是有效的代码吗?

问题描述可以在here找到,不过是关于情况(2)。我对案例(1)感兴趣。

【问题讨论】:

  • built-in operator[](long, const char *) ...那是...这是怎么回事...?
  • 据我所知,应该没有歧义。你用什么编译器?
  • 请问什么版本的C++
  • @bolov timsong-cpp.github.io/cppwp/n4659/over.built#14 T&amp; operator[](std::ptrdiff_t, T*);,不是吗?

标签: c++ language-lawyer


【解决方案1】:

GCC 是错误的。模板案例不应该有任何区别。

[over.match.best]/1 说:

如下定义ICSi(F):

  • ...

  • 让 ICSi(F) 表示将列表中的第 i 个参数转换为可行函数 F 的第 i 个参数的类型的隐式转换序列。 [over.best.ics] 定义了隐式转换序列和 [over.ics.rank] 定义了一个隐式转换序列比另一个转换序列更好或更差的转换序列意味着什么。

鉴于这些定义,如果对于所有参数 i,ICSi(F1) 不是比 ICSi(F2) 更差的转换序列,则将可行函数 F1 定义为比另一个可行函数 F2 更好的函数,和 ...

两个可行的候选人是

int         operator[](X&,             std::string); // F1
const char& operator[](std::ptrdiff_t, const char*); // F2

...而且ICS1(F1)(X -&gt; X&amp;)比ICS1(F2)(X -&gt; std::ptrdiff_t)好,不管X -&gt; std::ptrdiff_t是不是通过模板转换函数,但是ICS2(F1)( const char[4] -&gt; std::string) 比 ICS2(F2) (const char[4] -&gt; const char*) 差。所以没有一个函数比另一个函数更好,导致歧义。

这已被报告为GCC bug

【讨论】:

  • 要明确(帮助其他可能无法立即获得它的人,就像我没有):存在歧义,因此代码不应编译。 GCC 不将模板转换函数识别为调用 F2 的有效转换路径的一部分,因此它看不到任何歧义并编译。链接的 GCC 错误中描述了这种错误识别。
  • 这一课的教训是,您不应该将强制转换运算符用于您还定义了operator [] 的任何整数类型。这是应该放弃恕我直言的 C 时代错误之一。
【解决方案2】:

问题是每条路径都有一次转化:

  • 首先从"abc"std::string,然后是operator[] 呼叫。
  • 第二个从xstd::ptrdiff_t,然后是operator[] 一个std::ptrdiff_t 和一个const char*

所以解决方法是让转换运算符explicit:

int operator[](const std::string& str) { return x + str[0]; }
template <typename T>
explicit operator T() { return x; } // (1) fails only in clang

【讨论】:

  • @LightnessRacesinOrbit 很公平。
  • @LightnessRacesinOrbit 抱歉,没有关注您的评论。
  • 我的意思是我不再 100% 确定,因为我错过了 xX 而不是 long。所以另一个用户定义的转换在这里发挥作用
  • 是的,因此 explicit 解决方案无需添加额外的文字即可工作。
  • 当然显式转换有其自身的问题,但从长远来看可能会更好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-03
  • 2023-03-16
相关资源
最近更新 更多