Представьте себе случай, когда один (по той или иной причине) хотел реализовать функцию, которая действует как прокси при вызове функций.Можно реализовать его как шаблон, который принимает указатель на функцию и все ее аргументы.Однако, если есть функция, которая принимает указатель const
на что-то (например, int const*
), и вы пытаетесь передать простой указатель на функцию (например, int*
), вывод шаблона завершается неудачей из-занеоднозначный аргумент шаблона.Пример:
#include <utility>
template <class... TArgs>
void foo (void (*pFunc)(TArgs...), TArgs... args)
{
pFunc (std::forward<TArgs> (args)...);
}
void bar (int const* pInt) {}
int main ()
{
int a = 5;
foo (bar, &a);
}
Производит:
ошибка C2672: 'foo
': не найдена соответствующая перегруженная функция
ошибка C2782: 'void foo(void (__cdecl *)(TArgs...),TArgs...)
': параметр шаблона 'TArgs
' является неоднозначным
примечание: см. объявление 'foo
'
примечание: может быть 'const int*
'
примечание: или'int*
'
Даже если простой вызов bar (&a)
будет успешным из-за неявного преобразования между int*
и int const*
.
Да, я понимаю, чтоМожно указать типы аргументов напрямую (например, (foo<int const*> (bar, &a)
), но, учитывая тот факт, что произвольное количество аргументов принимается, такой список будет довольно длинным и, по моему личному мнению, будет выглядеть уродливо.
Другим вариантом было бы заполнить код с помощью const_cast
s, где бы ни потребовалось такое преобразование, но это также привело бы к раздуванию кода нежелательными способами (или, другими словами, также выглядело бы некрасиво).).
Третье решение, о котором я мог бы подумать, - это предоставить 2 пакета параметров, например:
template <class... TArgs, class... TArgs2>
void foo (void (*pFunc)(TArgs...), TArgs2... args)
{
pFunc (std::forward<TArgs2> (args)...);
}
Что решило бы непосредственную проблему неявного преобразования аргументов, но привело бы к другой проблеме - ошибкам компилятора, указывающим на реализацию foo
вместо вызова foo
в случае несовпадения аргументов функции, чтоусложнить определение того, где именно в базе кода был сделан несовпадающий вызов.Пример ошибок, которые я получаю, если использую приведенный выше пример и следующую main
функцию:
int main ()
{
float b = 5;
foo (bar, &b);
}
ошибка C2664: 'void (const int *)
': невозможно преобразовать аргумент 1 из 'float *
'до' const int *
'
примечание: указанные типы не связаны;для преобразования требуется reinterpret_cast, приведение в стиле C или приведение в функциональном стиле
примечание: см. ссылку на создание экземпляра шаблона функции 'void foo<const int*,float*>(void (__cdecl *)(const int *),float *)
'
Итак, вопрос в том,какая-то магия шаблонов, которая позволила бы ему работать так, как я ожидаю, учитывая ограничения и соображения, которые я изложил выше?Или я просто неразумно отношусь к своим требованиям?