使用模板转换运算符时,重载运算符'[]'的使用不明确

Bha*_*rag 11 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"];
}
Run Code Online (Sandbox Code Playgroud)

我使用命令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.
Run Code Online (Sandbox Code Playgroud)

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

问题的描述可以在这里找到,但它是关于情况(2).我对案例(1)感兴趣.

xsk*_*xzr 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更好的函数,并且 ......

两个可行的候选人是

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

......并且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错误.


Mat*_*her 7

问题是每条路径都有一个转换:

  • 首先从"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
Run Code Online (Sandbox Code Playgroud)