Добавление изменений перегрузки, которые выбраны при перегрузке
Я пытаюсь понять, как правила выбора перегрузки приводят к следующему (неинтуитивному) поведению. Когда у меня есть следующие функции:
#include <iostream>
// Overload 1
template<class T>
void write(T data)
{
std::cout << "Called write(T data)" << std::endl;
}
// Overload 2
template<class T, class ...U>
void write(T&& obj, U&&... objs)
{
std::cout << "Called write(T&& obj, U&&... objs)" << std::endl;
}
int main(int, char**)
{
int j = 0;
write(j);
return 0;
}
void write(T data)
перегрузка void write(T data)
Перегрузка 1). Я думаю, что это имеет смысл для меня: кандидаты на выбор перегрузки являются void write<T>(T)
T = int
и void write<T,U>(T&)
T = int, U = <>
. И write(T)
и write(T&)
были бы одинаково специализированными, но Overload 2 имеет пустой пакет параметров, поэтому выбирается Overload 1. Однако, если добавить третью перегрузку:
#include <iostream>
// Overload 0
void write(const int& data)
{
std::cout << "Called write(const int& data)" << std::endl;
}
// Overload 1
template<class T>
void write(T data)
{
std::cout << "Called write(T data)" << std::endl;
}
// Overload 2
template<class T, class ...U>
void write(T&& obj, U&&... objs)
{
std::cout << "Called write(T&& obj, U&&... objs)" << std::endl;
}
int main(int, char**)
{
int j = 0;
write(j);
return 0;
}
Затем внезапно появляется void write(T&& obj, U&&... objs)
(перегрузка 2). Почему добавление перегрузки, которая не выбрана, изменяется, какая перегрузка действительно выбрана?
Если единственные кандидаты были void write<T,U>(T&)
T = int, U = <>
и void write(const int&)
Я понимаю, почему выбрана void write<T,U>(T&)
, поэтому, возможно, что-то о добавлении дополнительной перегрузки предотвращает участие void write(T data)
от выбора перегрузки? Если да, то почему?
Поскольку это, похоже, специфическое поведение компилятора, это наблюдалось на gcc 7.3.0.
Еще одно интересное поведение: если функции переупорядочиваются таким образом, что новая перегрузка помещается между исходными двумя (то есть Overload 1, Overload 0, Overload 2), то gcc отклоняет ее с call of overloaded 'write(int&) is ambiguous
. Если функции переупорядочиваются так, что последняя перегрузка является последней (то есть перегрузка 1, затем перегрузка 2, затем перегрузка 0), то write(const int& data)
выбрана.
Ответы
Ответ 1
Я думаю, что это ошибка GCC:
Перегрузки:
- Перегрузка 0:
write(const int&)
- Перегрузка 1:
write(T) [T=int] → write(int)
- Перегрузка 2:
write(T&&,U&&...) [T=int&,U=[]] → write(int&)
Перегрузка 0 лучше, чем перегрузка 1, потому что перегрузка 0 не является специализированной функцией шаблонов.
Перегрузка 1 лучше, чем перегрузка 2, потому что перегрузка 1 является шаблоном функции, более специализированным, чем перегрузка 2.
Перегрузка 2 лучше, чем перегрузка 0, потому что cv-определитель типа параметра перегрузки 2 int&
является подмножеством одной из перегрузок 0: const int&
.
Таким образом, вызов неоднозначен, о чем сообщает Clang.
Чтобы упростить, наилучшая жизнеспособная функция здесь оценивается в 4 этапа при сравнении двух функций:
- Проверьте, какая из них является лучшей последовательностью конверсии (все конверсии - это преобразование идентичности здесь), тогда, если ранжирование конверсии одинаково:
- Убедитесь, что между двумя привязками привязки один лучше другого, тогда, если он не является ссылочной привязкой или два привязки не являются дифференцируемыми,
- Проверьте, является ли одна из функций специализированной специализацией, а другая - нет, тогда, если две из них являются специализациями шаблонов
- Проверьте, является ли одна из специализаций более специализированной, чем другая.