Reputation: 23
I am starting to work with templates and I am trying to figure out why the following does not work.
class testclass1
{
public:
template <typename...Ts>
using TFunc = std::function<void(Ts*...)>;
template <typename...Ts>
void SetFunction(TFunc<Ts...> tf)
{
// Do something
}
};
template<typename...Ts>
class testclass2
{
public:
using TFunc = std::function<void(Ts*...)>;
void SetFunction(TFunc tf)
{
// Do something
}
};
void some_function(std::string*, double*)
{
}
int main()
{
testclass1 tc1;
testclass2<std::string, double> tc2;
testclass1::TFunc<std::string, double> tf1 = some_function;
tc1.SetFunction<std::string, double>(tf1);
tc1.SetFunction<std::string, double>(testclass1::TFunc<std::string, double>{tf1});
tc2.SetFunction(some_function);
tc1.SetFunction<std::string, double>(some_function);
}
All works fine except the last version
tc1.SetFunction<std::string, double>(some_function);
which is actually what I would like to do since it has much less boilerplate and since I don't want testclass1 to require template arguments.
The last line gives the following compilation error:
No matching member function for call to 'SetFunction'
I don't understand what is the additional "type-parameter-0-0". Intuitively, this looks much like problems linked to an implicit *this pointer, but I don't see why TFunc would be a member function and have an implicit *this pointer (or is this linked to std::function?). Also, moving the code template <typename...Ts> using TFunc = std::function<void(Ts*...)>;
outside of testclass1 does not change anything.
From browsing different related Q&As, it seems that you cannot use std::function with template functions. Is this the problem here?
Is there a way to make tc1.SetFunction<std::string, double>(some_function);
work within testclass1?
Thanks in advance.
UPDATE: I'm trying to wrap my head around the explanation, I think I sort of get it but then---thinking about it---I'm still not 100% sure. Consider the following code:
template <typename...Ts>
using test_tuple = std::tuple<Ts...>;
template <typename...Ts>
void test_func(test_tuple<Ts...> tup)
{
// Do something
// e.g., PrintTuple(tup);
}
int main()
{
test_tuple<std::string, double> tt{"Hi", 3.1459f};
test_func<std::string>(tt);
}
In that case, only std::string
is specified in the parameter pack and the double is deduced (so not all parameters are specified in the pack). But why does it work in this case and I don't get an error?
Upvotes: 2
Views: 504
Reputation: 85361
it seems that you cannot use
std::function
with template functions
No, the issue has nothing to do with std::function
.
The problem is the failed deduction of the template parameter pack. Normally, when you specify all template arguments explicitly, no deduction is performed.
tc1.SetFunction<std::string, double>(some_function); // no deduction needed, right?
But in case of a pack, a deduction is always performed. It's because it's possible to specify some pack arguments and let the compiler deduce the rest, and there's no way to know if you specified only some or all the arguments.
But std::function<void(std::string*, double*)>
can't be deduced from void(*)(std::string*, double*)
(and user-defined conversions are not considered during deduction). Hence the error:
template argument deduction/substitution failed:
mismatched types 'std::function<void(Ts* ...)>' and 'void (*)(std::string*, double*)'
(note - there is no mention of "type-parameter-0-0" in GCC 11 output, so your compiler might be outdated).
Now, to fix this, we can introduce a non-deduced context to the parameter of SetFunction
. Then the substitution will be performed from the provided type arguments.
For example using an identity template:
template<typename T>
struct type_identity {
using type = T;
};
class testclass1
{
public:
template <typename...Ts>
using TFunc = std::function<void(Ts*...)>;
template <typename...Ts>
void SetFunction(typename type_identity<TFunc<Ts...>>::type tf)
{
// Do something
}
};
With regard to the update and the follow-up question - test_tuple
parameters are happily deduced from a test_tuple
argument - there's no conversion involved, it's just pattern matching. But std::function
is a class, it can be constructed from a function pointer, but these are different types, so one cannot be deduced from the other.
Upvotes: 2