Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
532 views
in Technique[技术] by (71.8m points)

c++ - "ambiguous overload for 'operator[]'" if conversion operator to int exist

I'm trying to implement the vector like and the map like [] operator for a class. But I get error messages from my compilers (g++ and clang++). Found out that they only occurs if the class has also conversion operators to integer types.

Now I have two problems. The first is that I don't know why the compiler can't distinguish between [](const std::string&) and [](size_t) when the class has conversion operators to ints. The second... I need the conversion and the index operator. How to fix that?

works:

#include <stdint.h>
#include <string>

struct Foo
{
    Foo& operator[](const std::string &foo) {}
    Foo& operator[](size_t index) {}
};

int main()
{
    Foo f;
    f["foo"];
    f[2];
}

does not work:

#include <stdint.h>
#include <string>

struct Foo
{
    operator uint32_t() {}
    Foo& operator[](const std::string &foo) {}
    Foo& operator[](size_t index) {}
};

int main()
{
    Foo f;
    f["foo"];
    f[2];
}

compiler error:

main.cpp: In function 'int main()':
main.cpp:14:9: error: ambiguous overload for 'operator[]' in 'f["foo"]'
main.cpp:14:9: note: candidates are:
main.cpp:14:9: note: operator[](long int, const char*) <built-in>
main.cpp:7:7: note: Foo& Foo::operator[](const string&)
main.cpp:8:7: note: Foo& Foo::operator[](size_t) <near match>
main.cpp:8:7: note:   no known conversion for argument 1 from 'const char [4]' to 'size_t {aka long unsigned int}'
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem is that your class has a conversion operator to uint32_t, so the compiler does not know whether to:

  1. Construct a std::string from the string literal and invoke your overload accepting an std::string;
  2. Convert your Foo object into an uint32_t and use it as an index into the string literal.

While option 2 may sound confusing, consider that the following expression is legal in C++:

1["foo"];

This is because of how the built-in subscript operator is defined. Per Paragraph 8.3.4/6 of the C++11 Standard:

Except where it has been declared for a class (13.5.5), the subscript operator [] is interpreted in such a way that E1[E2] is identical to *((E1)+(E2)). Because of the conversion rules that apply to +, if E1 is an array and E2 an integer, then E1[E2] refers to the E2-th member of E1. Therefore, despite its asymmetric appearance, subscripting is a commutative operation.

Therefore, the above expression 1["foo"] is equivalent to "foo"[1], which evaluates to o. To resolve the ambiguity, you can either make the conversion operator explicit (in C++11):

struct Foo
{
    explicit operator uint32_t() { /* ... */ }
//  ^^^^^^^^
};

Or you can leave that conversion operator as it is, and construct the std::string object explicitly:

    f[std::string("foo")];
//    ^^^^^^^^^^^^     ^

Alternatively, you can add a further overload of the subscript operator that accepts a const char*, which would be a better match than any of the above (since it requires no user-defined conversion):

struct Foo
{
    operator uint32_t() { /* ... */ }
    Foo& operator[](const std::string &foo) { /* ... */ }
    Foo& operator[](size_t index) { /* ... */ }
    Foo& operator[](const char* foo) { /* ... */ }
    //              ^^^^^^^^^^^
};

Also notice, that your functions have a non-void return type, but currently miss a return statement. This injects Undefined Behavior in your program.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...