简体   繁体   中英

Passing a function to a variadic function template

Consider the following function templates:

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

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

And the following function:

void bar(int n) {}

Why does the following occur:

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

The compilation error is (gcc-8 with 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);

Using a dummy template

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

makes foo0<void, int>(bar<int>); compile fine in gcc-8 but gives an error using clang with Apple LLVM version 10.0.0 (clang-1000.11.45.5).

The clang error is

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) {}

Why does the following occur [?]

Take in count that when you call

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

foo0() and foo1() are expecting a std::function and bar is a pointer to a function that can be converted to a std::function but isn't a std::function .

In the foo1() case, you explicit both RetType and ArgType template parameters, so the compiler can convert bar to a std::function<void(int)> and all goes well.

But the foo0() case is different because the template parameter ArgTypes... is a variadic one and calling foo0<void, int>(bar) you don't explicit the full ArgTypes... variadic list but only the first type.

If I'm not wrong, the problem is that the compiler try to deduce the rest of ArgTypes... from the bar argument but bar isn't a std::function so the compiler is unable to deduce the rest of ArgTypes... , so the error.

I suppose that

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

or simply

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

or (C++17 only) also

foo0(std::function{bar});

should compile because, calling foo0() this way, the function receive a std::function so the compiler can completely deduce the template parameters.

I don't understand how the version with bar() with a dummy template parameter

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

can compile with g++-8 and I suppose it's a g++ bug.

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

the fix is:

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) {}

and now your foo0<void, int>(bar) compiles.

The general problem is that saying foo0<void, int> , you aren't saying " RetType is void and ArgTypes... is int . You are saying ArgTypes... starts with int .

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

the above compiles fine.

...

Another approach in is to add another overload.

Leave this one:

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

but add:

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)} );
}

here we wrap F into a construction-guided std::function .

Your call to foo2<int, void>( bar ) now calls foo2<void, int, decltype(bar)&> , the 2nd overload. Then it proceeds to construct a std::function from it, and so long as the signatures match exactly it works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM