如何将运算符作为参数传递

时间:2010-03-31 10:25:27

标签: c++

我必须从文件中加载doubles数组,将每个元素乘以表中的值(不同元素的不同值),对其进行一些处理,反转乘法(即除法) )然后将数据保存回文件。

目前,我用两种不同的方法实现乘法和除法过程。现在幕后有一些额外的工作,但除了发生乘法/除法的特定陈述之外,其余代码是相同的。可以想象,通过这种方法,您必须非常小心地进行任何更改。周围的代码并不简单,因此它是手动编辑每个方法或将更改从一个方法复制到另一个方法并记住更改*和/运算符的情况。

经过太多的近距离调用后,我厌倦了这个,并希望创建一个实现公共逻辑和两个包装函数的通用函数,它们将哪个运算符用作参数。

我最初的方法是使用函数指针:

void MultiplyData(double data)  
{ TransformData(data, &(operator *));  } 

void DivideData(double data)  
{ TransformData(data, &(operator /));  }  

void TransformData(double data, double (*func)(double op1, double op2))  
{  /* Do stuff here... */ } 

但是,我不能将运算符作为指针传递(这是因为它是本机类型的运算符吗?),所以我尝试使用函数对象。最初我认为multiplies中的divides<functional>仿函数是理想的:

void MultiplyData(double data)  
{  
    std::multiplies<double> multFunct;  
    TransformData(data, &multFunct);  
}

void DivideData(double data)  
{  
    std::divides<double> divFunct;  
    TransformData(data, &divFunct);  
}

void TransformData(double data, std::binary_function<double, double, double> *funct)  
{  /* Do stuff here... */ } 

正如您所看到的,我试图使用基类指针以多态方式传递函子。问题是std::binary_function没有为要实现的子类声明operator()成员。

是否有我缺少的东西,或者是实现我自己的仿函数heirarchy的解决方案(这看起来真的比它的价值更麻烦)?

6 个答案:

答案 0 :(得分:11)

使TransformData成为模板函数:

template <typename F>
typename F::result_type TransformData(double data, F f) { ... }

这样称呼它:

double MultiplyData(double data) {
    return TransformData(data, std::multiplies<double>());
}

std :: binary_function是一个标记类。它的主要目的不是提供基类接口,而是将一些typedef注入到仿函数式类中(通过继承),这使得它们可以被标准库的其他部分使用。

答案 1 :(得分:3)

您可能希望查看使用Boost.Bind或Boost.Function,以便您可以执行以下操作:

TransformData(boost::function<double(double, double)> func) {
  // use func like a function that 
  // returns a double and takes two 
  // double parameters
}

答案 2 :(得分:1)

可能有一个更通用的解决方案,但在这种情况下,我会使用这样一个事实,即除法与乘法相反,即x/C == x * (1/C)

答案 3 :(得分:1)

首先:TransformData应该是使用静态多态而不是动态的模板函数。 binary_function只是“标记”,这些倍数和除数不会超过其operator() - 因为它没有。{/ p>

template<typename Func>
void TransformData(double *data,Func f)
{
   for(int i=0;i<some_data_size;i++)
      data[i]=f(data[i],otherdata[i]);
}

现在f将被正确使用。倍数,除数或任何其他。

然后你打电话

TransformData(data,std::multiples<double>());
TransformData(data,std::divides<double>());
TransformData(data,some_other_functional);

答案 4 :(得分:1)

我会考虑使用std::transform而不是TransformData。正如你所写的那样,TransformData需要比这种情况下真正需要的更紧密的耦合(继承)。如果你使用继承,这意味着你的乘法和除法函数必须是虚函数,(在乘法或除法这样简单的情况下)可能会增加很大的开销。

std::transform不需要虚函数调用。有了它,要调用的函数是一个模板参数,这意味着只要你传递的任何内容都支持函数调用的普通语法(即在名称后加上括号)它就可以工作。 / p>

由于transform也使用迭代器,因此您可以在从文件中读取时将其直接应用于输入:

transform(istream_iterator<double>(infile),
          istream_iterator<double>(),
          coefficients.begin(),
          data.begin(), 
          std::multiply);

// do the other work on data

transform(data.begin(), 
          data.end(), 
          coefficients.begin(), 
          ostream_iterator<double>(outfile),
          std::divide);

另一种考虑的可能性是使用std::valarray代替。使用valarray,代码可以简单到:data *= coefficients;data /= coefficients;

答案 5 :(得分:0)

正如Marcelo建议的那样,除了我不会将函数对象作为参数传递,而是在内部构造为:

template <typename TransformationFunctor>
TransformData(double data) 
{  
    TransformationFunctor f;
    ...
}

然后用作:

MultiplyData(double data)
{ 
    TransformData< std::multiplies<double> >(data); 
}