Порядок аргументов шаблона C++, влияющих на компиляцию

Я только что обнаружил новую ошибку компиляции в своем коде после обновления 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);
}

Несвязанные: как правило, не называйте свои функции так же, как функции стандартной библиотеки. Или, по крайней мере, поместите их в свое собственное namespace вместо глобального пространства имен.

user12002570 25.07.2024 16:47

Причина предупреждения @user12002570 заключается в том, что реализациям разрешено помещать функцию std в глобальное пространство имен. Обычно это не проблема для части стандартной библиотеки C++, но функции, унаследованные от формы C, имеют тенденцию определяться в глобальном пространстве имен, и реализации помещают их в std с помощью объявлений using. Поэтому помещение функций с одинаковыми именами в полученное глобальное пространство имен может привести к конфликтам.

Pete Becker 25.07.2024 16:58

Однажды я назвал переменную strlen, потому что она содержала длину строки. После долгой суеты и разочарования из-за совершенно причудливой серии ошибок компилятора я обнаружил, что библиотечная функция strlen вообще не является функцией, а представляет собой макрос. Поскольку обычные правила области видимости, которые позволили бы мне использовать мою переменную strlien в области видимости, в которой также не требовалось бы использовать функцию strlen, не применимы к макросам, у меня был очень плохой день. Извлеченный урок: не используйте повторно имена библиотечных функций.

user4581301 25.07.2024 19:10

Действительно, мой реальный код содержит эти функции в пространстве имен, однако это было удалено в рамках создания минимального воспроизводимого примера.

Phil Rosenberg 25.07.2024 19:31

Я думаю, что разрешение перегрузки, делающее более ограниченное лучше, чем менее ограниченное, работает только в том случае, если порядок вывода аргументов одинаков. Когда вы поменяли типы шаблонов местами, у вас есть две перегрузки, одна из которых не лучше другой. Кто-нибудь найдет соответствующий раздел Стандарта.

Gene 25.07.2024 19:47
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
5
75
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Частичный порядок шаблонов функций определен в разделе 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), или если параметры функции, которые позиционно соответствуют между двумя шаблоны не однотипны, ни один шаблон не более специализированный, чем другой.

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

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