dummydev dummydev - 2 months ago 14
C++ Question

`no matching function for call` upon parameter pack recursive expansion

Please consider the following program:

#include <iostream>


template <int I, typename T, typename ...Args>
struct foo {
static void bar(const T& t0, const T& t1, const Args&... args)
{
std::cout << "( " << t0 << ", " << t1 << " ) ";
foo::bar(args...);
}
};

template <int I, typename T>
struct foo<I, T> {
static void bar(const T& t0, const T& t1)
{
std::cout << "( "<< t0 << ", " << t1 << " ) " << std::endl;
}
};


int main() {
foo<1, int, float>::bar(0, 1, 18.f, -7.f);
return 0;
}


I would expect it to compile fine and output the following (or so) at runtime:

( 0, 1 ) ( 18, -7 )


However, this code produces the following output via
g++ -std=c++14 -pedantic -Wall -Wextra parampack.cpp
(GCC 5.3.0):

parampack.cpp: In function ‘int main()’:
parampack.cpp:23:45: error: no matching function for call to ‘foo<1, int, float>::bar(int, int, float, float)’
foo<1, int, float>::bar(0, 1, 18.f, -7.f);
^
parampack.cpp:6:17: note: candidate: static void foo<I, T, Args>::bar(const T&, const T&, const Args& ...) [with int I = 1; T = int; Args = {float}]
static void bar(const T& t0, const T& t1, const Args&... args)
^
parampack.cpp:6:17: note: candidate expects 3 arguments, 4 provided
parampack.cpp: In instantiation of ‘static void foo<I, T, Args>::bar(const T&, const T&, const Args& ...) [with int I = 1; T = int; Args = {float}]’:
parampack.cpp:23:25: required from here
parampack.cpp:9:17: error: no matching function for call to ‘foo<1, int, float>::bar(const float&)’
foo::bar(args...);
^
parampack.cpp:6:17: note: candidate: static void foo<I, T, Args>::bar(const T&, const T&, const Args& ...) [with int I = 1; T = int; Args = {float}]
static void bar(const T& t0, const T& t1, const Args&... args)
^
parampack.cpp:6:17: note: candidate expects 3 arguments, 1 provided


I tried all kind of variations and permutations like:

template <int I, typename T, typename U, typename ...Args>
struct foo {
static void bar(const T& t0, const T& t1, const U& u0, const U& u1, const Args&... args)
{
std::cout << "( " << t0 << ", " << t1 << " ) ";
foo::bar(u0, u1, args...);
}
};


...but I cannot seem to make it compile and I'm not really sure what exactly happens.

Why doesn't the compiler figure out what functions to instantiate?

Answer

To start with, you have the wrong number of arguments - as the compiler suggests. foo<1, int float>::bar() has the signature:

static void bar(int const&, int const&, float const& );

That's 3 arguments. You're passing in 4. Hence the straightforward error.

What you apparently are trying to do is to pick 2 arguments at a time. In this case, the trailing arguments in bar are not the same as the trailing arguments in foo, so you want:

template <int I, typename T, typename ...Args>
struct foo {
    template <class... Extra>
    static void bar(const T& t0, const T& t1, const Extra&... args)
    {
        std::cout << "( " << t0 << ", " << t1 << " ) ";
        foo::bar(args...); // <== (*)
    }
};

That will bring in the second problem: the recursive call to foo::bar(). The foo there is the injected-class-name of the primary template. That will never actually get you into your base case specialization. So there, you want:

foo<I, Args...>::bar(args...);