简体   繁体   中英

C++ Variadic Template Basics

I'm trying to code a numeric method that can accept a function as an argument that itself has arbitrary arguments. The best way to do this seems to be with a variadic template. This https://stackoverflow.com/a/15960517/3787488 answer is nearly exactly what I need, but my code wont compile.

This is my test case;

#include<iostream>
#include<vector>
#include<fstream>
#include<functional>
#include<iomanip>


double testfunction(double x, double k);

template<typename... Ts>
using custom_function_t = double(*) (double, Ts...);

template< typename... Ts>
double test( custom_function_t<Ts...> f, Ts... args, double min, double max, int m, int n)
{
    double ans=0;
    double step=(max-min)/100.00;
    for (double x=min;x<=max;x=x+(max-min)/100)
    {
        ans=ans+(step/6.0)*(f(x, args...)+4*f(x+0.5*step, args...)+f(x+step, args...));
    }
    return(ans);
}

int main()
{
    double ans=0;
    std::cout<<test(testfunction,2.0,0.0,1.0,0,0)<<endl;
    return(0);
}

double testfunction(double x, double k)
{
    double ans=0;
    ans=x*x*k;
    return(ans);
}

Where the function 'test' should take the function 'testfunction' and numerically integrate it (integration of 2*x^2 from 0 to 1=2/3).

Compiling with gcc 4.7.3 c++11 I get the errors;

note: template<class ... TS> double test (custom_function_t<Ts ...>, Ts ..., double, double, int, int)
note: template argument deduction/substitution failed:
note: candidate expects 5 arguments, 6 provided

In C++ (since 2011), something like this is best done using a lambda, caught via a template parameter, which can be any callable object:

#include<iostream>
#include<iomanip>
#include<cassert>

template<typename Func>   // anything that allows operator()(double)
double test(Func const&func, double x, const double max,
            const unsigned num_intervals=100)
{
  assert(num_intervals>0);
  const double dx=(max-x)/num_intervals;
  const double dx_half=0.5*dx;
  const double dx_third=dx/3.0;
  const double dx_two_third=dx_third+dx_third;
  double sum = 0.5*dx_third*func(x);
  for(unsigned i=1; i!=num_intervals; ++i) {
    sum += dx_two_third * func(x+=dx_half);
    sum += dx_third     * func(x+=dx_half);
  }
  sum+=dx_two_third* func(x+=dx_half);
  sum+=0.5*dx_third* func(x+=dx_half);
  return sum;
}

double testfunction(double, double);

int main()
{
  std::cout<<std::setprecision(16)
           <<test([](double x) { return testfunction(x,2.0); }, 0.0,1.0)
           <<std::endl;
}

double testfunction(double x, double k)
{
  return x*x*k;
}

Note also that I avoided to evaluate the functions more than once for the same value.

The compiler can't deduce the size of the parameter pack from the supplied arguments unless the pack is at the end.

As you've discovered, it works if you re-order the arguments.

Another option is to save it from having to deduce them by giving the arguments explicitly:

test<double>(testfunction, 2.0, 0.0, 1.0, 0, 0)

I'm not sure why GCC can't deduce the arguments from the function pointer you pass, but the EDG compiler can't either, giving this error:

"var.cc", line 20: error: no instance of function template "test" matches the
          argument list
            argument types are: (double (*)(double, double), double, double,
                      double, int, int)
      test(testfunction, 2.0, 0.0, 1.0, 0, 0);
      ^

My build of Clang 3.8.0 crashes on the original code, and 3.5.0 rejects it. If I get rid of the alias template and declare test as

template< typename... Ts>
double test( double(*f)(double, Ts...), Ts... args, double min, double max, int m, int n)

Then Clang 3.50 and 3.80 both compile it happily.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM