Я только что обнаружил новую ошибку компиляции в своем коде после обновления Visual Studio. Я сократил свой код до, как мне кажется, минимального примера. У меня есть две шаблонные функции, обе называются pow
— первая принимает два параметра любого типа и вызывает std::pow
с ними. Второй использует концепцию для переопределения первого шаблона, поэтому, если второй параметр соответствует концепции, вместо этого вызывается он.
В моем коде по какой-то причине два параметра шаблона располагались в порядке, противоположном тому, как они появлялись в списке параметров. Раньше компилировалось, но недавно перестало компилироваться с ошибкой 'pow': ambiguous call to overloaded function
. Однако, если я поменяю порядок параметров шаблона, чтобы он соответствовал параметрам функции, он компилируется нормально.
Почему это компилируется так, а не иначе?
Вот минимальный пример
#include <cmath>
template<class T, class U>
double pow(const T& base, const U& power)
{
return std::pow(base, power);
}
template<class T>
concept HasValue =
requires(std::remove_cvref_t < T > t)
{
t.value;
};
//this causes an ambiguous call to overloaded function compilation error
template<HasValue T, class U>
double pow(const U& s, const T& a)
{
return std::pow(s, a.value);
}
//this works with no compilation error
//template<class U, HasValue T>
//double pow(const U& s, const T& a)
//{
// return std::pow(s, a.value);
//}
class MyClass
{
public:
double value;
};
int main()
{
MyClass myClass;
myClass.value = 2.0;
pow(10.0, myClass);
}
Причина предупреждения @user12002570 заключается в том, что реализациям разрешено помещать функцию std
в глобальное пространство имен. Обычно это не проблема для части стандартной библиотеки C++, но функции, унаследованные от формы C, имеют тенденцию определяться в глобальном пространстве имен, и реализации помещают их в std
с помощью объявлений using. Поэтому помещение функций с одинаковыми именами в полученное глобальное пространство имен может привести к конфликтам.
Однажды я назвал переменную strlen
, потому что она содержала длину строки. После долгой суеты и разочарования из-за совершенно причудливой серии ошибок компилятора я обнаружил, что библиотечная функция strlen
вообще не является функцией, а представляет собой макрос. Поскольку обычные правила области видимости, которые позволили бы мне использовать мою переменную strlien
в области видимости, в которой также не требовалось бы использовать функцию strlen
, не применимы к макросам, у меня был очень плохой день. Извлеченный урок: не используйте повторно имена библиотечных функций.
Действительно, мой реальный код содержит эти функции в пространстве имен, однако это было удалено в рамках создания минимального воспроизводимого примера.
Я думаю, что разрешение перегрузки, делающее более ограниченное лучше, чем менее ограниченное, работает только в том случае, если порядок вывода аргументов одинаков. Когда вы поменяли типы шаблонов местами, у вас есть две перегрузки, одна из которых не лучше другой. Кто-нибудь найдет соответствующий раздел Стандарта.
Частичный порядок шаблонов функций определен в разделе 13.7.7.3 Стандарта. Цитирую (из N4981) абзац 2:
Частичный порядок выбирает, какой из двух шаблонов функций больше специализирован, чем другой, путем преобразования каждого шаблона по очереди (см. следующий абзац) и выполнение вывода аргументов шаблона с помощью тип функции. Процесс вычета определяет, является ли один из шаблоны более специализированы, чем другие. Если да, то чем больше специализированный шаблон — тот, который выбирается при частичном заказе процесс. Если оба вывода успешны, частичное упорядочение выбирает более ограниченный шаблон (если он существует), как определено ниже.
В следующих параграфах подробно рассматриваются преобразования, применяемые к параметрам шаблона, и способы синтеза уникального типа.
В пункте 6 описан порядок преобразованных шаблонов. Тот случай, когда у вас есть
template<class T, class U>
double pow(const T& base, const U& power);
template<class U, HasValue T>
double pow(const U& s, const T& a)
падает до 6,4
(6.4) — В противном случае, если один шаблон более ограничен, чем другой (13.5.5), более ограниченный шаблон более специализирован, чем шаблон другой.
Случай, когда у вас обратный порядок типов шаблонов
template<class T, class U>
double pow(const T& base, const U& power);
template<HasValue T, class U>
double pow(const U& s, const T& a);
переходит к правилам обратного порядка 6.2 и приземляется в 6.2.2
(6.2.2) — В противном случае, если соответствующие параметры шаблона списки параметров шаблона не эквивалентны (13.7.7.2), или если параметры функции, которые позиционно соответствуют между двумя шаблоны не однотипны, ни один шаблон не более специализированный, чем другой.
и поскольку ни один из шаблонов не является более специализированным, чем другой, это приводит к ошибке неоднозначности, выдаваемой компиляторами.
Несвязанные: как правило, не называйте свои функции так же, как функции стандартной библиотеки. Или, по крайней мере, поместите их в свое собственное
namespace
вместо глобального пространства имен.