type deducing only the arguments types for a variadic template

72 Views Asked by At

I am trying to apply a variadic template function to a tuple. I found following solution that works for C++14 (see https://stackoverflow.com/a/37100646/2712726).

#include <tuple>
template <typename T, typename U> void my_func(T &&t, U &&u) {}
int main(int argc, char *argv[argc]) {
  std::tuple<int, float> my_tuple;
  std::apply([](auto &&... args) { my_func(args...); }, my_tuple);
  return 0;
}

I also found many solutions for C++11 but non of those compiled. So I implemented my own solution, which also does not compile.

Following code shows my attempt. My question is why type deduction in the last statement u.apply(f) fails and if I can help the compiler somehow.

#include <iostream>
#include <tuple>

using namespace std;

template <typename... T>
struct parameter_pack : public parameter_pack<T>... {
    parameter_pack(const T&... t) : parameter_pack<T>(t)... {}

    // C++11
    template <template <typename...> class Func>
    void apply(Func<T...> f) {
        f(parameter_pack<T>::h...);
    }
    //c++14 works
    // template <typename Func>
    // void apply(Func f) {
    //     f(parameter_pack<T>::h...);
    // }

    // UPDATE:
    // This solution from Holy Black Cat Works great with C++11 too
    //void apply(void (*f)(const T&...)) {
    //    f(parameter_pack<T>::h...);
    //}
};

template <typename H>
struct parameter_pack<H> {
    parameter_pack(const H& h) : h(h) {}
    const H& h;
};

template <typename... T>
parameter_pack<T...> make_parameter_pack(const T&... t) {
    return parameter_pack<T...>(t...);
}

// test function f()
template <typename H>
void f(const H& h) {
    std::cout << h << std::endl;
}

template <typename H, typename... T>
void f(const H& h, const T&... t) {
    f(h);
    f(t...);
}

int main() {
    auto u = make_parameter_pack(1, 1.1, "hello");
    std::cout << std::endl;
    // C++11
    u.apply(f); // compiler error

    //C++14 works
    // auto lambda = [&](const auto&... args) {
    //    f(args...);
    // };
    // u.apply(lambda);
}

The compiler says:

<source>:49:7: error: no matching member function for call to 'apply'
   49 |     u.apply(f);
      |     ~~^~~~~
<source>:12:10: note: candidate template ignored: couldn't infer template argument 'Func'
   12 |     void apply(Func<T...> f) {
      |          ^
1 error generated.
Compiler returned: 1

UPDATE I have added Holy Cat's solution and marked as UPDATE in the code. That works.

1

There are 1 best solutions below

0
On BEST ANSWER

f is a function template, and you can't take an address of a "blueprint for functions". You could take the address of, let's say, f<int> but not of f.

Why does it work with a lambda? Because lambdas are just syntactic sugar for callable structs: you'd be passing an object which exists and lives somewhere in memory, the fact that the call operator is a template has no impact. Were you to pass lambda::operator() or any variation of that, you'd come back to the same problem as above.