Reputation: 43
I'm trying to use variadic template to refactor some of my code, but the compiler has "no matching function for call" error. Below is a simplified version (it may not make sense for functionality, but an example to reproduce the error):
// base case
void testFunc(int i) { std::cout << i << std::endl; }
template <class T, class... Args> void testFunc(int i) {
T t = 0;
std::cout << t << std::endl;
testFunc<Args...>(i);
}
int main() {
testFunc<int, long, float>(1);
return 0;
}
The error messages:
main.cpp:9:3: error: no matching function for call to 'testFunc'
testFunc<Args...>(i);
^~~~~~~~~~~~~~~~~
main.cpp:9:3: note: in instantiation of function template specialization 'testFunc<float>' requested here
main.cpp:9:3: note: in instantiation of function template specialization 'testFunc<long, float>' requested here
main.cpp:13:3: note: in instantiation of function template specialization 'testFunc<int, long, float>' requested here
testFunc<int, long, float>(1);
^
main.cpp:6:40: note: candidate template ignored: couldn't infer template argument 'T'
template <class T, class... Args> void testFunc(int i) {
^
1 error generated.
It looks like that the unwrapping of template parameters is working, and stops at the base case. But I have defined the base case. Why there is no matching function?
Upvotes: 4
Views: 1961
Reputation: 10982
As explained before the problem comes from the fact that testFunc<int>(int i)
tries to call testFunct<>(int t)
However in C++ testFunct<>(int t)
is different from testFunct(int t)
Also note that in C++ you can not partially specialize functions, as explained here for instance.
One solution that is close to your approach is to define
// Stop recursion
template <class T>
void testFunc(int i)
{
T t = 0;
std::cout << t << " " << typeid(T).name() << std::endl;
}
and to avoid ambiguous definition thanks to SFINAE
// instantiated only when Args... is not "empty"
template <class T, class... Args>
typename std::enable_if<sizeof...(Args)>::type testFunc(int i)
{
T t = 0;
std::cout << t << " " << typeid(T).name() << std::endl;
testFunc<Args...>(i);
}
This is perfectly legal C++11 and it follows closely your initial guess
Complete running code: to compile with g++ -std=c++11 testVariadic.cpp; ./a.out
#include <iostream>
#include <type_traits>
template <class T>
void testFunc(int i)
{
T t = 0;
std::cout << t << " " << typeid(T).name() << std::endl;
}
template <class T, class... Args>
typename std::enable_if<sizeof...(Args)>::type testFunc(int i)
{
T t = 0;
std::cout << t << " " << typeid(T).name() << std::endl;
testFunc<Args...>(i);
}
int main()
{
testFunc<int, long, float>(1);
return 0;
}
Output:
0 i
0 l
0 f
Upvotes: 1
Reputation: 40160
For a single type, your function is not well-defined since it tries to call void testFunct<>(int)
which differ from void testFunc(int i)
.
You could test the size of your parameter pack before recursing using C++17's constexp if
:
template <class T, class... Args> void testFunc(int i) {
T t = 0;
std::cout << t << std::endl;
if constexpr (sizeof...(Args) > 0) {
testFunc<Args...>(i);
} else {
testFunc(i);
}
}
Upvotes: 3
Reputation: 66230
The problem is that calling
testFunc<Args...>(i);
you call the template version of testFunc()
, not the base case version.
And when Args...
is empty, there isn't a template version available.
To solve this problem... if you can use C++17, you can use if constexpr
, as suggested by YSC.
For C++11 and C++14, I propose to use the template partial specialization of a struct
.
The following is a full working example
#include <iostream>
// base case
template <typename...>
struct foo
{ static void bar (int i) { std::cout << i << std::endl; } };
// recursive case
template <typename T, typename ... Args>
struct foo<T, Args...>
{
static void bar (int i)
{
std::cout << T{} << std::endl;
foo<Args...>::bar(i);
}
};
int main()
{
foo<int, long, float>::bar(1);
}
Upvotes: 3