Reputation: 2568
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) {}
Upvotes: 4
Views: 191
Reputation: 275350
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 c++17 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.
Upvotes: 1
Reputation: 66200
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.
Upvotes: 2