Home > Software engineering >  Is there a clean way to forward template parameters to a templated function?
Is there a clean way to forward template parameters to a templated function?

Time:07-04

The following works

#include <iostream>

template<typename A, typename B> double func1(B x, B y) 
{
    A a = 3.0f;
    return a   x   y;
}

template<typename A, typename B> double func2() 
{
    B x = 5.0;
    B y = 8.0;
    return func1<float>(x, y);
}

int main() 
{
    std::cout << func2<float, double>() << "\n";
}

However, what if I would like func1 to be substituted with another similar function? One with the same number and type of arguments and template parameters. I tried template<template<... like below, and it didn't seem to work.

#include <iostream>

template<typename A, typename B> double func1(B x, B y) 
{
    A a = 3.0f;
    return a   x   y;
}

template<template <typename, typename> typename func, class A, class B>
double func2() 
{
    B x = 5.0;
    B y = 8.0;
    return func<A, B>(x, y);
}

int main()
{
    std::cout << func2<func1, float, double>() << "\n";
}

It would be nice if a pattern like the one above worked.


Even better if func, could be partially templatized and still deduce from the type of the arguments like

#include <iostream>

template<typename A, typename B> double func1(B x, B y)
{
    A a = 3.0f;
    return a   x   y;
}

template<template <typename, typename> typename func, class A, class B>
double func2()
{
    B x = 5.0;
    B y = 8.0;
    return func<A>(x, y);
}

int main() 
{
    std::cout << func2<func1, float, double>() << "\n";
}

Any ideas on how to get the 2nd snippet to work?

Could you do so with template deduction as well? Could c 20 concepts do magic here?

CodePudding user response:

Function templates have some restrictions that class templates do not have. Consider that the type of func1<A,B> alone does not help you. You'd need the concrete function to call it. With a class template on the other hand, the type (an instantiation of the template) is all you need to know to use it.

Your 2nd code snippet works if you only change func1 to be a functor, a class with operator():

#include <iostream>

template<typename A, typename B>
struct func1  {
    double operator()(B x, B y){
      A a = 3.0f;
      return a   x  y;
    } 
};

template<template <typename, typename> typename func, class A, class B>
double func2(){
  B x = 5.0;
  B y = 8.0;
  return func<A,B>{}(x,y);
}

int main(){
  std::cout << func2<func1, float,double>() << "\n";
}

To deduce B from the parameters you can make operator() a template:

#include <iostream>


template <typename A>
struct func1  {
template<typename B>
double operator()(B x, B y){
  A a = 3.0f;
  return a   x  y;
} 
};

template<template <typename> typename func, class A, class B>
double func2(){
  B x = 5.0;
  B y = 8.0;
  return func<A>{}(x,y);
}

int main(){
  std::cout << func2<func1, float,double>() << "\n";
}

CodePudding user response:

Alternative to the templated functors given in the other answers, one can also use a generic lambda, along with the lambda's template support from .

// generic lambda
const auto func1 = [](auto x,auto y)
{
    decltype(x) a = 3.0f;
    return static_cast<double>(a   x   y);
};

template<typename Func, class A, class B>
double func2() {
    B x = 5.0;
    B y = 8.0;
    return Func{}.template operator()<A> (x, y);
    //          ^^^^^^^^^^^^^^^^^call of lambda like templated lambda!
}

This required the calling to be like:

std::cout << func2<decltype(func1), float, double>() << "\n";
           // ^^^^^^^^^^^^^^^^^^^passing type of the lambda

See live demo

CodePudding user response:

Even better if func, could be partially templatized and still deduce from the type of the arguments like

You can turn func1 into an functor with a template operator(), and partially specify the template parameter through .template operator()<A>.

#include <iostream>

struct Fun1 {
  template<typename A, typename B>
  double operator()(B x, B y){
    A a = 3.0f;
    return a   x   y;
  }
};

template<typename Func, class A, class B>
double func2(){
  B x = 5.0;
  B y = 8.0;
  return Func{}.template operator()<A>(x, y);
}

int main(){
  std::cout << func2<Fun1, float, double>() << "\n";
}
  • Related