2009-08-07 3 views
2

から鋳造、私はGCC 4.2.3で、次のエラーメッセージに遭遇しました。 (はい、私はそれが若干古いバージョンだと認識してんだけど、私は簡単にアップグレードすることはできません。)C++演算子のオーバーロード - LinuxへのWindowsコードを移植しながらクラス

main.cpp:16: error: call of overloaded ‘list(MyClass&)’ is ambiguous 
/usr/include/c++/4.2/bits/stl_list.h:495: note: candidates are: std::list<_Tp, _Alloc>::list(const std::list<_Tp, _Alloc>&) [with _Tp = unsigned char, _Alloc = std::allocator<unsigned char>] 
/usr/include/c++/4.2/bits/stl_list.h:484: note:     std::list<_Tp, _Alloc>::list(size_t, const _Tp&, const _Alloc&) [with _Tp = unsigned char, _Alloc = std::allocator<unsigned char>] 

私はこのエラーを生成するには、次のコードを使用しています。

#include <list> 
class MyClass 
    { 
    public: 
     MyClass(){} 

     operator std::list<unsigned char>() const { std::list<unsigned char> a; return a; } 
     operator unsigned char() const { unsigned char a; return a; } 

    }; 

    int main() 
    { 
     MyClass a; 
     std::list<unsigned char> b = (std::list<unsigned char>)a; 

     return 0; 
    } 

誰でもこのエラーが発生しましたか?もっと重要なことに、それを回避する方法は? (それは、このようなGetChar()GetList()などなどの機能を使用することによって、必ず、完全に過負荷を回避することが可能だが、私はそれを避けるためにしたいと思います。)

(ちなみに、「operator unsigned char()」を削除するとエラーが削除されます。)

答えて

1

それはあなたがキャストを削除し、私はオペレータのstd ::リストが実行されていることを確認した場合、正しくコンパイルされます。

int main() 
{ 
    MyClass a; 
    std::list<unsigned char> b = a; 

    return 0; 
} 

または、const参照にキャストした場合。

int main() 
    { 
     MyClass a; 
     std::list<unsigned char> b = (const std::list<unsigned char>&)a; 

     return 0; 
    } 
+0

なぜそうですか?私は、あなたが暗黙にオーバーロードされたキャストを呼び出すことができるという事実を知っています。 –

+0

申し訳ありませんが、明示的にお電話ください。 –

+0

実際にはわかりませんが、あなたが明示的に演算子を使用したいと言っているのではないかと思いますが、最初に標準演算子を使用してconst演算子を使用している可能性がありますが、 const&と競合しますが、実際はわかりません。 –

6

曖昧さはキャスト式の解釈から来ています。

変換を選択する場合、コンパイラは最初static_castスタイルのキャストを考慮し、このようになり、初期化を解決する方法を考えて:

std::list<unsigned_char> tmp(a); 

aは、Aユーザー定義の変換を持っているとして、この構造があいまいですstd::list<unsigned char>およびunsigned charおよびstd::list<unsigned char>は、const std::list<unsigned char>&をとるコンストラクタと、(unsigned charをプロモートできるコンストラクタ)の両方を持っています。 const std::list<unsigned_char>&にキャストするとき

、この初期化が考慮される:この場合

const std::list<unsigned_char>& tmp(a); 

std::list<unsigned_char>にユーザ定義の変換が選択されたとき、新たな参照は、変換の結果に直接結合することができます。タイプstd::list<unsigned char>の一時オブジェクトを選択したunsigned charにユーザ定義の変換を作成しなければならないであろう、これは、このオプション旧オプションよりも悪い変換シーケンスを行った場合。私は、次のあなたの例を簡略化してきました

2

typedef unsigned int size_t; 

template <typename T> 
class List 
{ 
public: 
    typedef size_t size_type; 
    List (List const &); 
    List (size_type i, T const & = T()); 
}; 

typedef List<unsigned char> UCList; 

class MyClass 
{ 
public: 
    operator UCList const() const; 
    operator unsigned char() const; 
}; 

void foo() 
{ 
    MyClass mc; 
    (UCList)mc; 
} 

最初のポイントは、標準だ。この場合、Cスタイルのキャストは、より適切なC++スタイルのキャストを使用する必要があることを定義し、ということですstatic_cast。したがって、上記のキャストはと同等です:

static_cast<UCList> (mc); 

はstatic_castの定義は言う:宣言"T t(e);"場合

式eが明示的にフォーム static_cast<T>(e)static_castを使用して、型Tに変換することができますいくつかの発明された一時変数のために、整形式である。 t(8.5)

したがって、キャストのセマンティクスは13.3.1.3から

UCList tmp (mc); 

我々はUCListに使用できる候補コンストラクタのセットを取得::のため

UCList (UCList const &)    #1 
UCList (size_type, T const & = T()); #2 
次に何が起こるかは、二つの別々のオーバーロードの解決手順、各変換演算子の一つです。

#1への変換:UCList const &のターゲット・タイプと、オーバーロード解決は「operator UCList const()」:次の変換演算子の間で選択し、「operator unsigned char()」。 unsigned charを使用すると、追加のユーザー変換が必要になるため、このオーバーロードステップで実行可能な機能ではありません。したがって、オーバーロードの解決に成功し、operator UCList const()を使用します。

#2への変換:size_tのターゲットタイプ。デフォルトの引数は、オーバーロードの解決には関与しません。オーバーロード分解能は、変換オペレータ "operator UCList const()"と "operator unsigned char()"の中から選択します。今回はUCListからunsigned intへの変換がないため、実行可能な関数ではありません。 unsigned charsize_tに昇格できます。したがって、この時間オーバーロードの解決に成功し、 "operator UCList const()"を使用します。

しかし、トップレベルに戻ると、mcからUCListに正常に変換された2つの個別の独立したオーバーロード解決手順があります。したがって結果はあいまいです。

最後の点を説明すると、この例は通常の過負荷解決の場合とは異なります。通常、1があります:nは、引数とパラメータの型との関係:

void foo (char); 
void foo (short); 
void foo (int); 

void bar() { 
    int i; 
    foo (i); 
} 

ここi=>chari=>shorti=>intがあります。これらは過負荷分解能によって比較され、int過負荷が選択されます。

上記の場合、私たちはm:nの関係にあります。標準では、個々の引数とすべての 'n'パラメータに対して選択するルールの概要を示していますが、それは終了するところで、異なる 'm'引数を使用する方法を決める方法を指定していません。

これはいくつかの意味がありますように!

UPDATE:

ここに初期化構文の二種類がある:

UCList t1 (mc); 
UCList t2 = mc; 

'T1' は直接initialiation(13.3.1.3)と全てコンストラクタは、過負荷に含まれていますセット。これは、複数のユーザー定義変換を持つようなものです。変換演算子のセットのコンストラクタのセットがあります。 (すなわち、m:n)。オブジェクトのコピー初期化の一部として8.5で指定された条件下で

、:構文は、コピー初期化(13.3.1.4)を使用し、ルールは異なる「T2」の場合

ユーザ定義の 変換を呼び出して、初期化式を初期化されるオブジェクトのタイプに変換することができます。 オーバーロード解決はここであり、ただ一つのタイプであるUCListなど考慮すべき変換演算子のオーバーロードのセットのみが存在する、すなわち

を呼び出されるユーザ定義の変換を選択するために使用されます。私たちはUCListの他のコンストラクタを考慮しません。

+0

新しいオブジェクト(コピー構築も同様)に対する直接のコピー構築と割り当ては、同じルールではうまくいきませんか? UCList t(mc); #これはあいまいです UCList mylist = mc; #これはあいまいではない –

+0

すばらしい答え。 あなたは何度も投票される価値があります:) –

+0

"これはちょっと意味がありますよ!" "(T)xはT(x)と同じことを理解するまで、 '!それはちょうど初期の設計ミスです。 – curiousguy

関連する問題