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

歧义来自cast-expression的解释。

选择转换时,编译器首先考虑static_cast样式转换并考虑如何解析如下所示的初始化:

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

这种构造是模棱两可的,因为a有一个用户定义的到 astd::list<unsigned char>和到 an 的转换,unsigned char并且std::list<unsigned char>有一个接受 aconst std::list<unsigned char>&的构造函数和一个接受 size_tunsigned char可以提升 an)的构造函数。

转换为 aconst std::list<unsigned_char>&时,会考虑以下初始化:

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

In this case, when the user-defined conversion to std::list<unsigned_char>is chosen, the new reference can bind directly to the result of the conversion. 如果用户定义的转换到选择的位置,则必须创建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 可以使用static_cast形式 的 a 显式转换为类型 T ,对于某些发明的临时变量 t (8.5)static_cast<T>(e)"T t(e);"

所以 cast 的语义与 for 相同:

UCList tmp (mc);

从 13.3.1.3 开始,我们得到了可以使用的候选构造函数集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 ()”。这次没有从UCListto的转换unsigned int,因此这不是一个可行的功能。Anunsigned char可以被提升到size_t,所以这次重载解析成功并将使用“ operator UCList const ()”。

但是,现在回到顶层,有两个独立且独立的重载解决步骤已成功从 转换mcUCList. 因此结果是模棱两可的。

为了解释最后一点,这个例子与正常的重载解决案例不同。通常参数和参数类型之间存在 1:n 的关系:

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

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

这里有i=>char和。这些通过重载分辨率进行比较,然后选择重载。i=>shorti=>intint

在上述情况下,我们有一个 m:n 关系。该标准概述了为每个单独的参数和所有“n”参数选择的规则,但这就是它的结尾,它没有指定我们应该如何决定使用不同的“m”参数。

希望这有点道理!

更新:

这里有两种初始化语法:

UCList t1 (mc);
UCList t2 = mc;

't1' 是直接初始化 (13.3.1.3),所有构造函数都包含在重载集中。这几乎就像拥有多个用户定义的转换一样。有一组构造函数一组转换运算符。(即 m:n)。

在 't2' 的情况下,语法使用复制初始化(13.3.1.4)并且规则不同:

在 8.5 中指定的条件下,作为类类型对象的复制初始化的一部分,可以调用用户定义的转换来将初始化表达式转换为正在初始化的对象的类型。重载解析用于选择要调用的用户自定义转换

在这种情况下,只有一个要键入,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 回答