【问题标题】:Pass pointer-to-template-function as function argument?将指向模板函数的指针作为函数参数传递?
【发布时间】:2010-11-19 22:54:29
【问题描述】:

假设我想要一个 C++ 函数对两个输入执行算术运算,并将它们视为给定类型:

伪:

function(var X,var Y,function OP)
{
 if(something)
  return OP<int>(X,Y);
 else if(something else)
  return OP<double>(X,Y);
 else
  return OP<string>(X,Y);
}

适合 OP 的函数可能是这样的:

template <class T> add(var X,var Y)
{
 return (T)X + (T)Y; //X, Y are of a type with overloaded operators
}

那么,问题是函数的签名是什么样的?如果运算符函数是非模板化的,我可以这样做,但我对这种额外的复杂性感到困惑。

【问题讨论】:

  • 查看模板模板参数。 (这不是错字。)
  • +1,这基本上就是通过OP的正确答案了。
  • 我添加了这个作为答案。我希望我没有把任何愚蠢的错误放进去。

标签: c++ templates operator-overloading


【解决方案1】:

模板函数不能作为模板参数传递。在将它传递给另一个模板函数之前,您必须手动推断此函数的模板参数。比如你有函数

T sum(T a, T b)
{
    return a + b;
}

你想把它传递给 callFunc:

template<typename F, typename T>
T callFunc(T a, T b, F f)
{
    return f(a, b);
}

你不能简单地写

int a = callFunc(1, 2, sum);

你必须写

int a = callFunc(1, 2, sum<int>);

为了能够在不编写 int 的情况下传递 sum,您必须编写一个函子 - 带有 operator() 的结构或类来调用您的模板函数。然后您可以将此函子作为模板参数传递。这是一个例子。

template<class T>
T sum(T a, T b)
{
    return a + b;
}
template<class T>
struct Summator
{
    T operator()(T a, T b)
    {
        return sum<T>(a, b);
    }
};
template<template<typename> class TFunctor, class T>
T doSomething(T a, T b)
{
    return TFunctor<T>()(a, b);
    //Equivalent to this:
    //TFunctor<T> functor;
    //return functor(a, b);
}


int main()
{
    int n1 = 1;
    int n2 = 2;
    int n3 = doSomething<Summator>(n1, n2); //n3 == 3
    return 0;
}

【讨论】:

  • 如果人们使用正确的术语,首先就不会出现这种混淆:“模板函数”不存在,它们是“函数模板”。也就是说,在您的示例callFunc(1, 2, sum); 中,您不是 将函数传递给callFunc,而是将模板 传递给它(正如您的示例显示的那样,可以将模板作为模板参数传递,但只能传递 class 模板,不能传递 function 模板)。
  • 嗯.. 没这么想。我相信最初的问题应该是“如何将函数模板作为函数模板参数传递”,对吧?
  • 那么,如果 sum 是类的非静态成员,你将如何使这个解决方案发挥作用?
  • 这绝对是绝妙的方法。我要做的唯一改进是使方法静态。例如,调用方法run。这将使您避免每次调用 sum 代码时都必须创建一个新的类实例
  • 您甚至可以将模板从班级移动到operator (),这样您就有了一个普通班级Summatortemplate &lt;class T&gt; T operator()(T a, T b)。然后,您可以为doSomething 使用常规模板而不是模板模板。
【解决方案2】:

你在找这个吗?

template<class T> T add(T X, T Y)
{
    return X + Y;
}

或者您正在寻找类似 add 的东西?

template<class T, class F>
T Apply(T x, T y, F f)
{
    return f( x, y );
}

调用方式:

int x = Apply( 2, 4, add<int> );

【讨论】:

  • 我相信他想通过 add 而不指定 int 作为模板参数。
【解决方案3】:

我有点困惑……为什么你的伪代码中有类型区分?

C++ 模板允许对模板进行完整的类型推导:

template <typename T, typename F>
T function(T x, T y, F op) {
    return op(x, y);
}

在这里,F 适合任何可以使用 () 函数调用语法调用的任何东西(尤其是函数),并且只接受两个 T 类型的参数(或隐式转换为它)。

【讨论】:

  • 我想这就是我的意思,没想到函数是模板参数。
  • 唯一的问题是 F 不能是带有未知模板参数的模板函数,它要么必须是非模板函数,要么必须是指定了所有模板类型的模板函数。
  • @izogfif 您还可以显式指定模板参数。可以使用模板元编程来推断它们。但无论如何,这似乎回答了 OP 的问题,无需进一步复杂化。
【解决方案4】:

我为此使用 lambdas。

auto add = [](const auto& lhs, const auto& rhs) {
    static_assert(std::is_arithmetic<typename std::decay<decltype(lhs)>::type>::value,
            "Needs to be arithmetic.");
    static_assert(std::is_arithmetic<typename std::decay<decltype(rhs)>::type>::value,
            "Needs to be arithmetic.");
    return lhs + rhs;
};

template<typename LHS, typename RHS, typename FUNC
    , typename OUT = typename std::result_of<FUNC(LHS, RHS)>::type>
constexpr OUT do_arithmetic(LHS lhs, RHS rhs, FUNC func) {
    return func(lhs, rhs);
}

constexpr auto t = do_arithmetic(40, 2, add);
static_assert(t == 42, "Wrong answer!");
static_assert(std::is_same<std::decay<decltype(t)>::type, int>::value,
        "Should be int.");

【讨论】:

  • 你应该得到更多的支持! Lambda 是解决这个问题的现代方法,而不是仿函数。
  • 从C++14开始,lambda函数(Generic lambda function)参数的类型可以声明为auto,是一个“generic lambda”(类似于模板化的lambda)
【解决方案5】:

我认为您正在寻找Strategy Pattern

【讨论】:

    【解决方案6】:
    template <class OP> void function(OP op)
    {
      // call with int
      op(1, 2);
      // or with double
      op(1.2, 2.3);
      // call with explicit template argument
      op.template operator()<int>(1, 2);
      op.template operator()<string>("one", "two");
    }
    
    struct Add
    {
      template <class T> T operator ()(T a, T b)
      {
        return a + b;
      }
    };
    
    function(Add());
    // or call with C++14 lambda
    function([](auto a, auto b) { return a + b; });
    

    【讨论】:

    • 这个答案是我的最爱,简单又够用!
    • 这个答案很有启发性,当你想更动态地对待一个函数时,例如给它的名字取别名,将它作为参数传递给其他函数,你需要把它变成一个仿函数:@987654321 @
    【解决方案7】:

    我不确定您问题中的 var 是什么意思。它肯定不是一个有效的 C++ 关键字,所以我认为它是类似于boost:any 的类型。此外,该函数缺少结果类型。我添加了另一个var,不管它是什么。您的解决方案可能如下所示:

    template< template<typename> class Func >
    var function(var X, var Y, Func OP)
    {
     if(something)
      return OP<int>(X,Y);
     else if(something else)
      return OP<double>(X,Y);
     else
      return OP<string>(X,Y);
    }
    

    有趣的模板参数是一个模板本身,因此它的名字是“模板模板参数”。您传入模板的名称,而不是实例。也就是说,你通过std::plus,而不是std::plus&lt;int&gt;

    return function( a, b, std::plus );
    

    【讨论】:

    • 由于编译错误,在 Visual C++ 2008、Visual C++ 2010 中不起作用。
    • @izogfif:现在成像,有一段时间,您提供了确切的编译器错误。可能有人过来了,看了看,明白了问题所在,并发布了解决方案。当然,我们不想要这个,所以你没有提供这个很好。
    • 好点。这是我尝试编译的代码和编译器引发的错误(在代码末尾):pastebin.com/YyhX9ruT
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-17
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    • 1970-01-01
    • 2013-11-18
    • 1970-01-01
    相关资源
    最近更新 更多