Передача функции в шаблон переменной функции - PullRequest
0 голосов
/ 04 февраля 2019

Рассмотрим следующие шаблоны функций:

template<typename RetType, typename... ArgTypes>
void foo0(std::function<RetType(ArgTypes...)> f) {}

template<typename RetType, typename ArgType>
void foo1(std::function<RetType(ArgType)> f) {}

И следующую функцию:

void bar(int n) {}

Почему происходит следующее:

 foo0<void, int>(bar);  // does not compile
 foo1<void, int>(bar);  // compiles fine

Ошибка компиляцииis (gcc-8 с C ++ 17):

error: no matching function for call to 'foo0<void, int>(void (&)(int))'
   foo0<void, int>(bar);
                      ^
note: candidate: 'template<class RetType, class ... ArgTypes> void foo0(std::function<_Res(_ArgTypes ...)>)'
 void foo0(std::function<RetType(ArgTypes...)> f) {}
      ^~~~
note:   template argument deduction/substitution failed:
note:   mismatched types 'std::function<void(_ArgTypes ...)>' and 'void (*)(int)'
   foo0<void, int>(bar);

Использование фиктивного шаблона

template<typename T>
void bar(int n) {}

делает foo0<void, int>(bar<int>); прекрасной компиляцией в gcc-8, но выдает ошибку с использованием clangс Apple LLVM версии 10.0.0 (clang-1000.11.45.5).

Ошибка лягушки

error: no matching function for call to 'foo0'
  foo0<void, int>(bar<int>);
  ^~~~~~~~~~~~~~~
note: candidate template ignored: could not match 'function<void (int, type-parameter-0-1...)>' against 'void (*)(int)'
void foo0(std::function<RetType(ArgTypes...)> f) {}

Ответы [ 2 ]

0 голосов
/ 04 февраля 2019
template<typename RetType, typename... ArgTypes>
void foo0(std::function<RetType(ArgTypes...)> f) {}

исправление:

template<class X>struct tag_t{using type=X;};
template<class X>using block_deduction = typename tag_t<X>::type;

template<typename RetType, typename... ArgTypes>
void foo0(block_deduction_t<std::function<RetType(ArgTypes...)>> f) {}

и теперь ваши foo0<void, int>(bar) компилируются.

Общая проблема заключается в том, что вы говорите foo0<void, int>, вы не говорите "RetType - это void, а ArgTypes... - это int. Вы говорите, что ArgTypes... начинается с int.

std::function<void(int, double)> x;
foo0<void, int>( x )

, выше скомпилировано отлично.

...

Другой подход в - добавить еще одну перегрузку.

Оставьте эту:

template<typename RetType, typename... ArgTypes>
void foo2(block_deduction_t<std::function<RetType(ArgTypes...)>> f) {}

но добавьте:

template<typename RetType, typename... ArgTypes, class F>
void foo2(F&& f) {
  return foo2<RetType, ArgTypes...>( std::function{std::forward<F>(f)} );
}
template<int unused, class F>
void foo2(F&& f) {
  return foo2( std::function{std::forward<F>(f)} );
}

здесь мы заключаем F в ориентируемый на конструкцию std::function.

Ваш вызов foo2<int, void>( bar ) теперь вызывает foo2<void, int, decltype(bar)&>, 2-ю перегрузку.он продолжает строить std::function из него, и пока сигнатуры точно совпадают, он работает.

0 голосов
/ 04 февраля 2019

Почему происходит следующее [?]

Учтите, что при вызове

 foo0<void, int>(bar); // compilation error
 foo1<void, int>(bar); // compile

foo0() и foo1() ожидают std::function и bar - это указатель на функцию, которая может быть преобразована в std::function, но не является std::function.

В случае foo1() вы явно указываете как RetType, так иArgType параметров шаблона, так что компилятор может преобразовать bar в std::function<void(int)>, и все идет хорошо.

Но случай foo0() отличается, поскольку параметр шаблона ArgTypes... является переменным ипри вызове foo0<void, int>(bar) вы не указываете полный список ArgTypes... variadic, а только первый тип.

Если я не ошибаюсь, проблема в том, что компилятор пытается вывести остальную часть ArgTypes...из аргумента bar, но bar не является std::function, поэтому компилятор не может вывести остальную часть ArgTypes..., поэтому ошибка.

Я полагаю, что

foo0<void, int>(std::function<void(int)>{bar});

или просто

foo0(std::function<void(int)>{bar});

или (только C ++ 17) также

foo0(std::function{bar});

должны компилироваться, потому что, вызывая foo0() таким образом, функция получилаea std::function чтобы компилятор мог полностью определить параметры шаблона.

Я не понимаю, как версия с bar() с фиктивным параметром шаблона

foo0<void, int>(bar<int>);

может компилироваться с g ++-8 и я предполагаю, что это ошибка g ++.

...