1

I try to pass to a variadic template function a list of references and pass it to another function. The code that I wrote is the following:

template <typename T>
void fun(cv::Point_<T> & pt) { pt.x++; pt.y++; }

template <class ... args>
void caller(args & ... list) {

    typedef typename std::tuple_element<0, std::tuple<args...> >::type T;

    std::array<std::reference_wrapper<T>, sizeof...(list)> values {list ...     };

    for(int i=0; i<values.size(); i++)
      fun(values[i]);

}

then I call the function caller in this way:

cv::Point2f a, b, c;

caller(a, b, c);

the compiler give me the following error:

No matching function for call to 'fun'
Candidate template ignored: could not match 'Point_' against 'reference_wrapper'

what I missing?

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
thewoz
  • 499
  • 2
  • 4
  • 23

4 Answers4

2

Although std::reference_wrapper<T> has an implicit conversion to T&, you cannot use both an implicit conversion and template argument deduction at the same time, and template argument deduction is necessary to call fun.

Try

fun(values[i].get());
aschepler
  • 70,891
  • 9
  • 107
  • 161
2

Even simpler is

template <typename...Args>
void caller(Args&...args)
{
    auto tmp = { (func(args),0)..., 0 };
}

This uses the fact that parameter pack expansion can occur in braced init lists. Since func() returns void, we cannot simply use { func(args)... }, but use (func(args),0) to have an int. Finally, the last 0 is to ensure that the code compiles (and does nothing) in case of an empty parameter pack.

You can generalise this and write a template that calls a given generic function for every element of a pack:

template <typename Func, typename...Args>
void call_for_each(Func &&func, Args&&...args)
{
    auto unused = { (func(std::forward<Args>(args)),0)...,0 };
}

which may be used like this (C++14)

int main()
{
    int    a=1;
    double b=2.4;
    auto func = [](auto&x) { std::cout<<' '<<x++; };
    call_for_each(func,a,b);
    std::cout<<'\n';
    call_for_each(func,a,b);
    std::cout<<'\n';
}

This uses a C++14 lambda (taking an auto argument). Note that the parameter pack must come last among the template parameters of call_for_each.

Walter
  • 44,150
  • 20
  • 113
  • 196
  • Works indeed. However, it is like Aramaic for me. But what about is not void and I need to sum the returning values? – thewoz May 06 '17 at 16:41
  • @Walter It seems the `call_for_each` cannot accept mutable lambda. Do you mind replacing `Func const&func` by `Func func` or maybe `Func &&func` ? Thanks. – Guillaume Racicot May 06 '17 at 19:25
1

Since the goal of this might be to iterate over all args, here's a more generic solution. We are going to implement for_pack:

template<typename... Args, typename F>
void for_pack(F function, Args&&... args) {
    using expand = int[];
    (void)expand{(function(std::forward<Args>(args)), void(), 0)..., 0};
}

This will execute function for every args in Args.

Now, your function caller is much more trivial to implement:

template <typename... args>
void caller(args&... list) {
    for_pack([&](cv::Point_<T>& arg){
        fun(arg);
    }, list...);
}
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
  • You don't need that `void(),` nor all the `expand` stuff, see my answer. – Walter May 06 '17 at 16:49
  • @Walter Yes you need the `void()`. If `function` happen to return an object that overloads `operator , (int)`, it can lead to nasty compilation error. By the way, your solution should trigger a warning about `tmp` not being used. – Guillaume Racicot May 06 '17 at 16:51
1

Since a google search for "c++ pass reference parameters to variadic template" gives this as first result, I'll put this generic solution here.

struct HH { /*...*/ void change_me() { /*...*/ } };

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

HH a, b, c;
..
    parms_r_refs(a, b, c);
..
slashmais
  • 7,069
  • 9
  • 54
  • 80