Вывод типа шаблона с помощью std::function

Я обнаружил следующее поведение с std::function и тип вычета, который был неожиданным для меня:

#include <functional>

template <typename T>
void stdfunc_test(std::function<T(T)> func) {};

int test_func(int arg)
{
    return arg + 2;
}

int main()
{
    stdfunc_test([](int _) {return _ + 2;});
    stdfunc_test(test_func);
}

Обе строки в main привести к ошибке:

ни один экземпляр шаблона функции "stdfunc_test" не соответствует списку аргументов

При попытке компиляции в Visual Studio 2015.

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

2 ответа

Решение

При выводе аргумента шаблона неявное преобразование не выполняется, кроме: temp.deduct.call

В общем, процесс вывода пытается найти значения аргументов шаблона, которые сделают выведенный A идентичным A (после преобразования типа A, как описано выше). Однако есть три случая, которые допускают разницу:

  • Если исходный P является ссылочным типом, то выведенный A (то есть тип, на который ссылается ссылка) может быть более квалифицированным по cv, чем преобразованный A.
  • Преобразованный A может быть другим указателем или указателем на тип члена, который может быть преобразован в выведенный A посредством преобразования указателя функции ([conv.fctptr]) и / или преобразования квалификации ([conv.qual]).
  • Если P является классом и P имеет форму simple-template-id, то преобразованный A может быть производным классом выведенного A. Аналогично, если P является указателем на класс вида simple-template-id, преобразованный A может быть указателем на производный класс, на который указывает выведенный A.

Однако, если параметр шаблона не участвует в выводе аргумента шаблона, будет выполнено неявное преобразование: ( http://eel.is/c++draft/temp.arg.explicit)

Неявное преобразование (пункт [conv]) будет выполнено для аргумента функции, чтобы преобразовать его в тип соответствующего параметра функции, если тип параметра не содержит шаблонных параметров, которые участвуют в выводе аргумента шаблона. [Примечание: параметры шаблона не участвуют в выводе аргументов шаблона, если они указаны явно.

Итак, если вы явно укажете аргумент шаблона, он должен работать:

stdfunc_test<int>([](int _) {return _ + 2;});
stdfunc_test<int>(test_func);

Вы можете использовать шаблоны для вывода сигнатуры функций и функторов:

#include<functional>

template<class T>
struct AsFunction
    : public AsFunction<decltype(&T::operator())>
{};

template<class ReturnType, class... Args>
struct AsFunction<ReturnType(Args...)> {
  using type = std::function<ReturnType(Args...)>;
};

template<class ReturnType, class... Args>
struct AsFunction<ReturnType(*)(Args...)> {
  using type = std::function<ReturnType(Args...)>;
};


template<class Class, class ReturnType, class... Args>
struct AsFunction<ReturnType(Class::*)(Args...) const> {
  using type = std::function<ReturnType(Args...)>;
};

template<class F>
auto toFunction( F f ) -> typename AsFunction<F>::type {
  return {f};
}

template <typename T>
void stdfunc_test(std::function<T(T)> func) {};

int test_func(int arg)
{
    return arg + 2;
}


int main()
{

    stdfunc_test( toFunction([](int _) {return _ + 2;}) );
    stdfunc_test( toFunction(test_func) );
    return 0;
}

Вы можете попробовать это в прямом эфире здесь: http://fiddle.jyt.io/github/d4ab355eb2ab7fc4cc0a48da261f0127

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