2

Windows コードを Linux に移植しているときに、GCC 4.2.3 で次のエラー メッセージが表示されました。(はい、少し古いバージョンであることは承知していますが、簡単にアップグレードすることはできません。)

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()」を削除すると、エラーが削除されます。)

4

3 に答える 3

6

あいまいさは、キャスト式の解釈に由来します。

変換を選択するとき、コンパイラは最初にstatic_castスタイル キャストを考慮し、次のような初期化を解決する方法を考慮します。

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

この構造は、aおよび anへaのユーザー定義の変換があり、a を取るコンストラクターと (anをプロモートできる)を取るコンストラクターの両方があるため、あいまいです。std::list<unsigned char>unsigned charstd::list<unsigned char>const std::list<unsigned char>&size_tunsigned char

にキャストする場合const std::list<unsigned_char>&、次の初期化が考慮されます。

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

この場合、ユーザー定義の変換をstd::list<unsigned_char>選択すると、新しい参照を変換の結果に直接バインドできます。選択された場所へのユーザー定義の変換unsigned charがタイプの一時オブジェクトをstd::list<unsigned char>作成する必要がある場合、このオプションは前のオプションよりも悪い変換シーケンスになります。

于 2009-08-07T10:55:59.467 に答える
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 の定義は次のように述べています。

式 e は、何らかの発明された一時変数 t (8.5) に対して、宣言が整形式である場合static_cast、形式の a を使用して明示的に型 T に変換できます。static_cast<T>(e)"T t(e);"

したがって、キャストのセマンティクスは次の場合と同じです。

UCList tmp (mc);

13.3.1.3 から、で使用できる候補コンストラクターのセットを取得しますUCList

UCList (UCList const &)              #1
UCList (size_type, T const & = T()); #2

次に行われるのは、変換演算子ごとに 1 つずつ、2 つの個別のオーバーロード解決手順です。

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

#2 への変換: のターゲット タイプsize_t。デフォルトの引数は、オーバーロードの解決には関与しません。オーバーロード解決は、変換演算子 " operator UCList const ()" と " operator unsigned char ()" の間で再度選択します。今回はandUCListからunsigned intand への変換がないため、実行可能な関数ではありません。Anunsigned charは に昇格できるsize_tため、今回はオーバーロードの解決が成功し、" operator UCList const ()" が使用されます。

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

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

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

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

ここに と がi=>charありi=>shortますi=>int。これらはオーバーロード解決によって比較され、intオーバーロードが選択されます。

上記の場合、m:n の関係があります。標準では、個々の引数とすべての「n」パラメーターについて選択するルールの概要が示されていますが、それで終わりであり、異なる「m」引数の使用を決定する方法は指定されていません。

これが意味をなすことを願っています!

アップデート:

ここでの 2 種類の初期化構文は次のとおりです。

UCList t1 (mc);
UCList t2 = mc;

't1' は直接の初期化 (13.3.1.3) であり、すべてのコンストラクターがオーバーロード セットに含まれます。これは、ユーザー定義の変換が複数あるようなものです。コンストラクターのセットと変換演算子のセットがあります。(つまり、m:n)。

「t2」の場合、構文はコピー初期化 (13.3.1.4) を使用し、ルールは異なります。

8.5 で指定された条件下では、クラス型のオブジェクトのコピー初期化の一部として、ユーザー定義の変換を呼び出して、初期化子式を初期化されるオブジェクトの型に変換できます。オーバーロードの解決は、呼び出されるユーザー定義の変換を選択するために使用されます

この場合、入力するのは 1 つUCListだけなので、考慮すべき変換演算子のオーバーロードのセット、つまり . UCList の他のコンストラクターは考慮しません。

于 2009-08-07T11:57:11.917 に答える
1

キャストを削除すると正しくコンパイルされ、演算子std::listが実行されていることを確認しました。

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;
     }
于 2009-08-07T10:15:49.280 に答える