【问题标题】:How can I do the type deduction on parameterized template function如何对参数化模板函数进行类型推导
【发布时间】:2020-01-03 07:51:08
【问题描述】:
#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;
}

我想将模板函数作为参数传递,如上所示。该代码有效。但是我必须在Add&lt;int&gt;等函数之后加上&lt;int&gt;

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

CallFuncs(1,2, Add, Mul);

【问题讨论】:

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


    【解决方案1】:

    不能直接做,但是可以把函数变成函数对象:

    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&lt;&gt;{} 中),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);
        }
    };
    

    【讨论】:

    • 请注意,由于整数参数,这将使用 int 而不是 double
    • 哇.. 很酷,谢谢伙计!我花了 5 个小时搜索和阅读书籍并尝试不同的编码方式,并在 10 分钟内得到了您的答案。
    • @AlanBirtles 我觉得很好。当我们调用 std::max(...) 时,我们仍然需要指定数字是 1.0 而不是 1。
    • @WL_Law,我从标准库中添加了一个使用此技术的示例。
    【解决方案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); }); }
    

    【讨论】:

      【解决方案3】:

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

      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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-15
        • 1970-01-01
        • 2015-09-19
        • 2018-12-05
        • 1970-01-01
        • 1970-01-01
        • 2014-04-17
        • 2020-07-09
        相关资源
        最近更新 更多