Разрешение перегрузки и пользовательское преобразование

Рассмотрим простой код:

struct A;
struct B {
  B(){}
  B(A const&){ }
};

struct A {
  operator int() const {return 0;};
};
void func(B){}
void func(char){}

int main()
{
func(A()); //ambiguous call oO
}

Прежде всего, я не уверен, правильно ли я все понимаю, поэтому поправьте меня каждый раз, когда вы ошибаетесь, пожалуйста.

Я понимаю, что это void func(B) должен был быть выбран, так как аргумент func является A который является типом класса, следовательно, тип требуемого преобразования - "Пользовательская последовательность преобразования"

Теперь из IBM C++ ref:

Пользовательская последовательность преобразования состоит из следующего:

  • Стандартная последовательность преобразования
  • Пользовательское преобразование
  • Вторая стандартная последовательность преобразования

Теперь есть два пользовательских преобразования, присутствующих B::B(const A&) а также A::operator int (const A&);

поэтому последовательность

-> A() -> B::B(const A&) -> Standard conversion (identity conversion)

-> A() -> A::operator int (const A&) -> Standard conversion (integral conversion)

поскольку интегральное преобразование хуже, чем преобразование идентичности, я думал void func(B) позвонил бы, но все же вызов неоднозначен.

Поэтому, пожалуйста, помогите мне, в какой момент я ошибаюсь и почему вызов неоднозначен. Большое спасибо:)

2 ответа

Решение

Две последовательности преобразования здесь, A -> B а также A -> int оба определены пользователем, потому что они работают через функции, которые вы определили.

Правило ранжирования определяемых пользователем последовательностей преобразования находится в 13.3.3.2 (N3797):

Пользовательская последовательность преобразования U1 является лучшей последовательностью преобразования, чем другая пользовательская последовательность преобразования U2 если они содержат одну и ту же пользовательскую функцию преобразования или конструктор, или они инициализируют один и тот же класс в совокупной инициализации и в любом случае вторая стандартная последовательность преобразования U1 лучше, чем вторая стандартная последовательность преобразования U2

Эти две последовательности преобразования не содержат одну и ту же пользовательскую функцию преобразования, и они не инициализируют один и тот же класс в агрегатной инициализации (поскольку одна инициализируется int).

Поэтому неверно, что одна последовательность стоит выше другой, поэтому этот код неоднозначен.

так что последовательность -> A() -> B::B(const A&) -> Стандартное преобразование (преобразование идентичности)

Нет! Выдержка из стандарта (черновик) [over.best.ics] (выделено мной):

  1. Если преобразования не требуются для сопоставления аргумента с типом параметра, последовательность неявного преобразования является стандартной последовательностью преобразования, состоящей из преобразования идентификаторов (13.3.3.1.1).

func(A()) это не личность, это определяется пользователем. Опять из стандарта, [[conv]]:

Для типов классов также рассматриваются определяемые пользователем преобразования; см. 12.3. Как правило, неявная последовательность преобразования (13.3.3.1) состоит из стандартной последовательности преобразования, за которой следует определенное пользователем преобразование, за которым следует другая стандартная последовательность преобразования.

Я думаю, у вас есть недоразумение о стандартных конверсиях. Они не имеют ничего общего с пользовательскими типами / классами. Стандартные преобразования предназначены только для встроенных типов: преобразование lvalue-в-значение, преобразование массива в указатель, преобразование функции в указатель, интегральное повышение, повышение с плавающей запятой, целочисленные преобразования, преобразования с плавающей запятой, преобразования с плавающей запятой, преобразования указателей, указатели на преобразования членов, логические преобразования и преобразования квалификации. A -> int это не что-то из перечисленного, а пользовательское преобразование. Стандарт пользовательских преобразований, [[class.conv]], т.е. 12.3:

Преобразования типов объектов класса могут быть определены конструкторами и функциями преобразования. Эти преобразования называются пользовательскими преобразованиями и используются для неявных преобразований типов (пункт 4), для инициализации (8.5) и для явных преобразований типов (5.4, 5.2.9).

У вас есть две пользовательские последовательности преобразования одного и того же ранга (см. Ответ MM, чтобы узнать почему), поэтому компилятор хочет, чтобы вы устраняли неоднозначность.

Другие вопросы по тегам