如何对参数化模板函数进行类型推导

时间:2020-01-03 07:51:08

标签: c++ templates template-argument-deduction

#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;
}

我想将模板函数作为参数传递,如上所示。该代码有效。但是,我必须在<int>之类的函数之后放置Add<int>

如果这是不可扣除的上下文,那么还有另一种方法可以让我这样写吗,其中AddMul仍然是模板函数

CallFuncs(1,2, Add, Mul);

3 个答案:

答案 0 :(得分:3)

您不能直接执行操作,但是可以将函数转换为函数对象:

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将根据ab的类型推导。在此示例中,它将为int。要获取double,您需要double参数:

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

或显式类型说明:

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

这与标准库(自C ++ 14起)中的“钻石”函子非常相似。例如,std::plus声明是

template<class T = void>
struct plus;

如果Tvoid(例如,在std::plus<>{}中),则plus::operator()推导参数和返回类型。典型的implementation看起来像这样(有一些小的简化):

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);
    }
};

答案 1 :(得分:2)

如果您可以将模板函数转换为函子类,则可以这样做:

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

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

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

那你就可以做

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

或者具有更多类似的语法:

constexpr Add add{};
constexpr Mul mul{};

void FunctionInvokeTest() { CallFuncs(1, 2, add, mul); }

如果您无法更改函数,则将它们包装在lambda中可能会有所帮助:

void FunctionInvokeTest() { CallFuncs(1, 2,
                                      [](auto lhs, auto rhs) { Add(lhs, rhs); },
                                      [](auto lhs, auto rhs) { Mul(lhs, rhs); }); }

答案 2 :(得分:2)

通过将函数参数移到模板中的解决方法

template <typename F>
using BinaryOp = void(F, F); // function type

// fns is now a pack of function objects instead of types
template <typename F, BinaryOp<F>... fns>
void CallFuncs(F a, F b) { 
  (fns(a, b), ...);
};

void FunctionInvokeTest() {
    // the functions are now passed as template arguments alongside the desired numeric type
    CallFuncs<float, Add, Mul>(1, 2); 
}

https://godbolt.org/z/DutpHk