Как неявно преобразовать указатель в константный указатель при выводе из шаблона - PullRequest
0 голосов
/ 16 ноября 2018

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

Итак, вопрос в том,какая-то магия шаблонов, которая позволила бы ему работать так, как я ожидаю, учитывая ограничения и соображения, которые я изложил выше?Или я просто неразумно отношусь к своим требованиям?

1 Ответ

0 голосов
/ 16 ноября 2018

Вы можете ограничить функцию:

template <class... TArgs, class... TArgs2>
enable_if_t<is_invocable_v<void(TArgs...),TArgs2...>>
foo (void (*pFunc)(TArgs...), TArgs2... args)
    {
    pFunc (forward<TArgs2> (args)...);
    }
...