Почему неоднозначно вызывать перегруженные ambig (long) и ambig (unsigned long) с целым литералом?

При компиляции

void ambig(  signed long) { }
void ambig(unsigned long) { }

int main(void) { ambig(-1); return 0; }

Я получаю

error C2668: 'ambig' : ambiguous call to overloaded function
    could be 'void ambig(unsigned long)'
    or 'void ambig(long)'
while trying to match the argument list '(int)'

Я знаю, что могу "исправить" его, сказав -1L вместо -1, но почему/как именно это считается двусмысленным в первую очередь?

Ответ 1

Вы передаете int этой перегруженной функции.

Хотя человеческая интуиция говорит, что ambig(signed long) должен быть предпочтительнее, потому что ваш ввод является отрицательным целым числом (которое не может быть представлено как таковое unsigned long), эти два преобразования фактически эквивалентны в "приоритете" в С++.

То есть, преобразование intunsigned long считается справедливым как intsigned long, и ни один из них не является предпочтительным для другого.

С другой стороны, если ваш параметр уже был long, а не int, то есть точное соответствие signed long, без необходимости преобразования. Это позволяет избежать двусмысленности.

void ambig(  signed long) { }
void ambig(unsigned long) { }

int main(void) { ambig(static_cast<long>(-1)); return 0; }

"Только одна из этих вещей".


[C++11: 4.13/1]: ( "Integer ранг преобразования" )

Каждый целочисленный тип имеет целочисленный ранг преобразования, определяемый следующим образом:

  • [..]
  • Ранг целочисленного типа со знаком должен быть больше ранга любого целочисленного типа со знаком с меньшим размером.
  • Ранг long long int должен быть больше ранга long int, который должен быть больше ранга int, который должен быть больше чем ранг short int, который должен быть больше ранга подписанного char.
  • Ранг любого беззнакового целочисленного типа должен равняться ранга соответствующего знакового целочисленного типа.
  • [..]

[Примечание. Integer ранг преобразования используется в определении интегральных повышений (4.5) и обычных арифметических преобразований (п. 5). -end note]

Разрешение перегрузки является сложным и определяется в [C++11: 13.3]; Я не буду вас утомлять, цитируя здесь большинство.

Здесь выделяется:

[C++11: 13.3.3.1/8]: Если для сопоставления аргумента с типом параметра не требуется преобразований, неявная последовательность преобразования является стандартной последовательностью преобразования, состоящей из преобразования идентичности (13.3.3.1.1).

[C++11: 13.3.3.1/9]: Если последовательность преобразований не может быть найдена для преобразования аргумента в тип параметра или преобразование иначе плохо сформировано, неявная последовательность преобразования не может быть сформирована.

[C++11: 13.3.3.1/10]: Если существует несколько различных последовательностей преобразований, каждая из которых преобразует аргумент в тип параметра, неявная последовательность преобразования, связанная с параметром, определяется как уникальная последовательность преобразования, обозначающая неоднозначную последовательность преобразования. С целью ранжирования неявных последовательностей преобразования, как описано в 13.3.3.2, неоднозначная последовательность преобразования рассматривается как определяемая пользователем последовательность, которая неотличима от любой другой пользовательской последовательности преобразования134. Если функция, использующая неоднозначную последовательность преобразований, выбрана в качестве лучшей жизнеспособной функции, вызов будет плохо сформирован, потому что преобразование одного из аргументов в вызове неоднозначно.

  • /10 - это тот случай, который вы испытываете; /8 - это случай, когда вы используете аргумент long.

Ответ 2

Константа -1 имеет тип int. Таким образом, вы вызываете ambig с int в качестве аргумента. ambig не имеет перегрузки, которая принимает int, поэтому нам придется посмотреть на неявные преобразования, которые мы могли бы сделать. int может быть неявно преобразован в long или unsigned long (между прочим), оба из которых будут действительными аргументами для ambig. Таким образом, компилятор не знает, какое преобразование нужно выбрать, и вам нужно вручную вручную (или использовать длинную константу (-1l) вместо константы int).

Тот факт, что -1 является отрицательным числом, не учитывает его, потому что компилятор не смотрит на значение аргументов, просто его тип.

Ответ 3

Потому что -1 имеет тип int. И int может быть неявно преобразован либо в signed long, либо unsigned long.