Разрешение перегрузки и пользовательское преобразование
Рассмотрим простой код:
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] (выделено мной):
- Если преобразования не требуются для сопоставления аргумента с типом параметра, последовательность неявного преобразования является стандартной последовательностью преобразования, состоящей из преобразования идентификаторов (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, чтобы узнать почему), поэтому компилятор хочет, чтобы вы устраняли неоднозначность.