重载运算符“ []”的使用与模板转换运算符不明确

时间:2019-02-08 11:32:36

标签: c++ language-lawyer

以下代码在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。在所有情况下,错误都是相同的。 c中的错误消息如下:

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)的。我对第一种情况感兴趣。

2 个答案:

答案 0 :(得分:9)

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更好的函数。 / strong> ...

两个可行的候选人是

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

...并且ICS1(F1)(X -> X&)优于ICS1(F2)(X -> std::ptrdiff_t),无论X -> std::ptrdiff_t是否通过模板转换功能,但是ICS2(F1)(const char[4] -> std::string)比ICS2(F2)(const char[4] -> const char*)差。因此,两种功能都不比另一种更好,从而导致模棱两可。

这被报告为GCC bug

答案 1 :(得分:7)

问题在于,每个路径上都有一次转化:

  • 首先从"abc"std::string,然后进行operator[]呼叫。
  • xstd::ptrdiff_t的第二个
  • ,然后是operator[]std::ptrdiff_t的{​​{1}}。

因此解决方案是使转换运算符const char*

explicit