Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I do the type deduction on parameterized template function

#include <iostream>

template <typename... Ts> void Print(Ts... args) { 
  (std::cout << ... << args) << std::endl;
}

template <typename T> void Add(T a, T b) { Print(a + b); } 

template <typename T> void Sub(T a, T b) { Print(a - b); } 

template <typename T> void Mul(T a, T b) { Print(a * b); } 

template <typename F, typename... Fns> void CallFuncs(F a, F b, Fns... fns) { 
  (fns(a, b), ...);
};

void FunctionInvokeTest() { CallFuncs(1, 2, Add<int>, Mul<int>); }

int main() { 
  FunctionInvokeTest();
  return 0;
}

I want to pass the template function as parameter shown as above. The code works. However I must put <int> after the function such as Add<int>.

If this is non-deductible context, then is there another way to allow me write like this, where the Add and Mul are still template functions?

CallFuncs(1,2, Add, Mul);
like image 786
WL_Law Avatar asked Mar 03 '23 23:03

WL_Law


1 Answers

You can't do it directly, but you can turn functions into function objects:

struct Add {
    template<typename T>
    void operator()(T a, T b) {
        Print(a + b); } 
};

struct Mul {
    template<typename T>
    void operator()(T a, T b) {
        Print(a * b); } 
};

template<typename F, typename... Fns>
void CallFuncs(F a, F b, Fns... fns) {
    (fns(a, b), ...);
};

void FunctionInvokeTest() { 
    CallFuncs(1, 2, Add{}, Mul{});
}

T will be deduced from the type of a and b. In this example it will be int. To get double, you need double parameters:

CallFuncs(1., 2., Add{}, Mul{});

or explicit type specification:

CallFuncs<double>(1, 2, Add{}, Mul{});

This is very similar to "diamond" functors in the standard library (since C++14). For example, the std::plus declaration is

template<class T = void>
struct plus;

If T is void (e.g., in std::plus<>{}), plus::operator() deduces argument and return types. Typical implementation looks like this (with some minor simplifications):

template<> struct plus<void> {
    template<typename Tp, typename Up>
    constexpr auto operator()(Tp&& t, Up&& u) const {
        return std::forward<Tp>(t) + std::forward<Up>(u);
    }
};
like image 177
Evg Avatar answered May 04 '23 00:05

Evg