如何在参数化的模板函数上进行类型推断

huangapple go评论74阅读模式
英文:

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

template <typename T> struct FunctionWrapper {
  template <typename... Args>
  static void Call(Args... args) {
    T()(args...);
  }
};

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

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

In the code above, I've introduced a FunctionWrapper template struct that helps you pass template functions like Add and Mul as arguments to CallFuncs without specifying the template parameters explicitly. This allows you to call CallFuncs(1, 2, Add, Mul); as you desired.

英文:
#include &lt;iostream&gt;

template &lt;typename... Ts&gt; void Print(Ts... args) { 
  (std::cout &lt;&lt; ... &lt;&lt; args) &lt;&lt; std::endl;
}

template &lt;typename T&gt; void Add(T a, T b) { Print(a + b); } 

template &lt;typename T&gt; void Sub(T a, T b) { Print(a - b); } 

template &lt;typename T&gt; void Mul(T a, T b) { Print(a * b); } 

template &lt;typename F, typename... Fns&gt; void CallFuncs(F a, F b, Fns... fns) { 
  (fns(a, b), ...);
};

void FunctionInvokeTest() { CallFuncs(1, 2, Add&lt;int&gt;, Mul&lt;int&gt;); }

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

I want to pass the template function as parameter shown as above. The code works. However I must put &lt;int&gt; after the function such as Add&lt;int&gt;.

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

答案1

得分: 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.0, 2.0, Add{}, Mul{});

或者使用显式的类型说明:

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

这与标准库中的“diamond”函数对象非常相似(自从 C++14 起)。例如,std::plus 的声明如下:

template<class T = void>
struct plus;

如果 Tvoid(例如,在 std::plus<>{} 中),plus::operator() 将推断参数和返回类型。典型的实现如下(经过一些简化):

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);
    }
};
英文:

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

struct Add {
    template&lt;typename T&gt;
    void operator()(T a, T b) {
        Print(a + b); } 
};

struct Mul {
    template&lt;typename T&gt;
    void operator()(T a, T b) {
        Print(a * b); } 
};

template&lt;typename F, typename... Fns&gt;
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&lt;double&gt;(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&lt;class T = void&gt;
struct plus;

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

template&lt;&gt; struct plus&lt;void&gt; {
    template&lt;typename Tp, typename Up&gt;
    constexpr auto operator()(Tp&amp;&amp; t, Up&amp;&amp; u) const {
        return std::forward&lt;Tp&gt;(t) + std::forward&lt;Up&gt;(u);
    }
};

</details>



# 答案2
**得分**: 2

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

```cpp
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); }); }
英文:

If you can transform your template function into functor class, you might do:

struct Add {
    template &lt;typename T&gt;
    void operator ()(T a, T b) const { Print(a + b); } 
};

struct Sub
{
    template &lt;typename T&gt; void operator() (T a, T b) const  { Print(a - b); } 
};

struct Mul
{
    template &lt;typename T&gt; void operator() (T a, T b) const { Print(a * b); } 
};

Then you can do

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

Or to have more similar syntax:

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

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

If you cannot change your function, wrapping them in lambda might help:

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

答案3

得分: 2

将函数参数移入模板的解决方法

template <typename F>
using BinaryOp = void(F, F); // 函数类型

// fns 现在是函数对象的包,而不是类型
template <typename F, BinaryOp<F>... fns>
void CallFuncs(F a, F b) { 
  (fns(a, b), ...);
};

void FunctionInvokeTest() {
    // 现在将函数作为模板参数传递,与所需的数字类型一起
    CallFuncs<float, Add, Mul>(1, 2); 
}

链接: https://godbolt.org/z/DutpHk

英文:

Workaround by moving function arguments into template

template &lt;typename F&gt;
using BinaryOp = void(F, F); // function type

// fns is now a pack of function objects instead of types
template &lt;typename F, BinaryOp&lt;F&gt;... fns&gt;
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&lt;float, Add, Mul&gt;(1, 2); 
}

https://godbolt.org/z/DutpHk

huangapple
  • 本文由 发表于 2020年1月3日 15:51:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/59574965.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定