Вопрос

При переносе кода 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()"удаляет ошибку.)

Это было полезно?

Решение

Он компилируется правильно, если вы удалите приведение, и я проверил, что оператор std::list выполняется.

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

    return 0;
}

Или если вы приведете его к константной ссылке.

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

        return 0;
     }

Другие советы

Двусмысленность возникает из-за интерпретации актерское выражение.

При выборе преобразования компилятор сначала учитывает static_cast приведение стиля и обдумывает, как разрешить инициализацию, которая выглядит следующим образом:

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

Эта конструкция неоднозначна, так как a имеет определяемое пользователем преобразование в std::list<unsigned char> и к unsigned char и std::list<unsigned char> имеет как конструктор, который принимает const std::list<unsigned char>& и конструктор, который принимает size_t (на что unsigned char можно продвигать).

При приведении к const std::list<unsigned_char>&, эта инициализация считается:

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

В этом случае, когда пользовательское преобразование в std::list<unsigned_char> выбран, новая ссылка может быть напрямую связана с результатом преобразования.Если пользовательское преобразование в unsigned char где выбран временный объект типа std::list<unsigned char> придется создать, и это делает этот вариант худшей последовательностью преобразования, чем первый вариант.

Я упростил ваш пример до следующего:

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 с помощью static_cast формы static_cast<T>(e) если декларация "T t(e);" хорошо сформировано, для некоторой изобретенной временной переменной T (8.5)

Таким образом, семантика приведения такая же, как и для:

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 ()".На этот раз конвертация из UCList к unsigned int и поэтому это нежизнеспособная функция.Ан unsigned char может быть повышен до size_t и поэтому на этот раз разрешение перегрузки прошло успешно и будет использоваться "operator UCList const ()".

Но теперь, вернувшись на верхний уровень, есть два отдельных и независимых шага разрешения перегрузки, которые успешно преобразуются из mc к 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».

Надеюсь, это имеет какой-то смысл!

ОБНОВЛЯТЬ:

Здесь есть два типа синтаксиса инициализации:

UCList t1 (mc);
UCList t2 = mc;

't1' - это прямая инициализация (13.3.1.3) и все конструкторы включены в набор перегрузки.Это почти похоже на наличие нескольких пользовательских преобразований.Есть набор конструкторов и набор операторов преобразования.(т.е.м:н).

В случае «t2» синтаксис использует инициализацию копирования (13.3.1.4) и другие правила:

В условиях, указанных в 8.5, как часть копирования инициализации объекта типа класса, можно вызвать пользовательское преобразование для преобразования выражения инициализатора в тип инициализированного объекта.Разрешение перегрузки используется для выбора определяемого пользователем преобразования, которое будет вызвано.

В этом случае нужно ввести только один, UCList, поэтому необходимо учитывать только набор перегрузок операторов преобразования, т.е.мы не рассматриваем другие конструкторы UCList.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top