2013-04-06 5 views
11

クラスのようなベクトルと[]演算子のようなマップを実装しようとしています。しかし、コンパイラからエラーメッセージが出ます(g ++とclang ++)。クラスが変換演算子も整数型に持つ場合にのみ発生することがわかりました。"変換演算子がintに存在する場合、 'operator []'のあいまいなオーバーロード

今私には2つの問題があります。最初は、なぜコンパイラが[](const std :: string &)とクラスに変換演算子をintと区別できるのか分からないということです。 2番目の...変換とインデックス演算子が必要です。誰もそれを修正する方法を知っていますか?

事前のおかげで、私からよろしく

作品:

#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]; 
} 

は動作しません:

#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]; 
} 

コンパイラエラー:

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}' 
+0

「キャスト演算子」のようなものはありません。問題のオペレータは**変換**です。キャストは、コンパイラに変換を指示するためにソースコードに書き込むものです。 –

+0

@Pete Beckerありがとうございました。 – r2p2

答えて

18

問題があることですあなたのクラスにはuint32_tへの変換演算子、コンパイラがするかどうかわからないので:

  1. std::stringを受け入れ、あなたの過負荷を文字列リテラルからstd::stringを構築して呼び出します。
  2. Fooオブジェクトをuint32_tに変換し、それを文字列リテラルのインデックスとして使用します。

オプション2は混乱に聞こえるかもしれないが、次の式は、C++で合法であることを考慮してください。

1["foo"]; 

これが原因で内蔵の添字演算子が定義されている方法です。

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.

したがって、上記式1["foo"]oに評価"foo"[1]、と等価である:C++ 11標準のパラグラフ8.3.4/6当り。あいまいさを解決するには、(C++ 11で)変換演算子explicitを作ることができ、次のいずれか

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

それともあなたはそれがあるように、その変換演算子を残して、明示的にstd::stringオブジェクトを構築することができます

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

struct Foo 
{ 
    operator uint32_t() { /* ... */ } 
    Foo& operator[](const std::string &foo) { /* ... */ } 
    Foo& operator[](size_t index) { /* ... */ } 
    Foo& operator[](const char* foo) { /* ... */ } 
    //    ^^^^^^^^^^^ 
}; 
012:(それがユーザ定義の変換を必要としないので)

または、上記のいずれかよりも良い一致だろうconst char*を受け付ける添字演算子のさらなる過負荷を追加することができ

また、関数にはvoid以外の戻り値型がありますが、現在はreturnステートメントがありません。これにより、未定義の動作がプログラムに挿入されます。

+0

今私の脳は痛い。しかし、私は今それを理解していると思う。ユーザーのためにできるだけ簡単にしたいと思った。 intまたはstd :: stringへの明示的キャストはそれを助けません。私は少し悲しい。 説明していただきありがとうございます。 – r2p2

+0

@ r2p2:あなたが意味することを理解していますが、 'uint_32'への'明示的な 'キャストはそれほど悪くありません。結局のところ、それは 'Foo f; if(f){/ * ... * /}; 'または' Foo f; std :: cout <<(f + f); 'をコンパイルします。 –

+0

@ r2p2:私の最後の更新では、(const char *を受け入れる追加のオーバーロードを使って)さらに別の方法を提案します。 –

2

問題はf["foo"]のように解決することができるということである。std::string"foo"を変換する(それはsこと)とFoo::operator[](const std::string&)を呼び出すf[s]を行う

  1. 演算子が可換性であるというよく知られている事実を利用して、fFoo::operator int()i)に変換し、i["foo"]に変換します。

どちらも1つのカスタムタイプ変換を持っているため、あいまいさがあります。

簡単な解決策は、さらに別のオーバーロードを追加することです:

Foo& operator[](const char *foo) {} 

さて、あいまいさが壊れているので、任意のカスタム型変換を必要とせずに新しいオーバーロードを呼び出しますf["foo"]を呼び出します。

注:タイプchar[4](タイプタイプ"foo")からchar*への変換は些細なものとみなされ、カウントされません。 a[b]char const*ためb[a]と同じであり、そして、あなたのクラスはuint32_tに変換されて、これはchar*std::stringに変換されるほど良い試合です - 他の回答で述べたように

2

は、あなたの問題は、デフォルトで[]通勤がということです。私はここに提供しています何

はあなたが過負荷が、それは必要があることを、あなたの信念にもかかわらず、呼び出されません、問題のまさにこのようなものを、持っているときは、「非常に魅力的な過負荷」を作るための方法です。だからここ

std::stringのための「非常に魅力的な過負荷」とFooです:

我々はあらゆるタイプを キャプチャしたテンプレートを作成し、その後、機能「文字列で検索」自立を作成
struct Foo 
{ 
    operator uint32_t() {return 1;} 
    Foo& lookup_by_string(const std::string &foo) { return *this; } 
    Foo& operator[](size_t index) {return *this;} 
    template< 
    typename String, 
    typename=typename std::enable_if< 
     std::is_convertible< String, std::string >::value 
    >::type 
    > Foo& operator[](String&& str) { 
    return lookup_by_string(std::forward<String>(str)); 
    } 
}; 

std::stringに変換することができます。

operator[]のテンプレート内でユーザー定義の変換を「隠す」ため、一致を確認するときにユーザー定義の変換は行われないため、ユーザー定義の変換が必要な操作(たとえばuint32_t[char*])よりも優先されます。事実、これは、引数と正確に一致しないオーバーロードよりも「魅力的」なオーバーロードです。

これは、あなたがconst Bar&を取り、Barstd::stringへの変換を持つ別の過負荷を持っている場合は、上記の過負荷があなたを驚かせるとBarに渡さ取り込むことができる、問題につながることができます - 右辺値と非const変数の試合の両方を上記の[]署名は[const Bar&]よりも優れています!

+0

私は本当にTemplate Metaprogrammingの本を読む必要があります。 – r2p2

関連する問題