简体   繁体   English

如何将函数应用于数组中的所有元素(在C ++模板类中)

[英]How to apply function to all elements in array (in C++ template class)

I have a template class that stores an array of numbers and I want to apply existing (scalar) functions to every element. 我有一个存储数字数组的模板类,我想将现有(标量)函数应用于每个元素。 For example, if we assume my class is std::vector, then I want to be able to call (for example) the std::cos function on all elements. 例如,如果我们假设我的类是std :: vector,那么我希望能够在所有元素上调用(例如)std :: cos函数。

Maybe a call would look like this: 也许一个电话看起来像这样:

std::vector<float> A(3, 0.1f);
std::vector<float> B = vector_function(std::cos, A);

NB I must also handle std::complex<> types (for which the appropriate complex std::cos function is called). 注意我还必须处理std :: complex <>类型(为其调用适当的复杂std :: cos函数)。

I found this answer which suggests taking the function type as a template parameter: 我发现这个答案建议将函数类型作为模板参数:

template<typename T, typename F>
std::vector<T> vector_function(F func, std::vector<T> x)

However, I couldn't get this to work at all (maybe because functions like std::sin and std::cos are both templated and overloaded?). 但是,我根本无法使用它(可能是因为像std :: sin和std :: cos这样的函数都是模板化和重载的?)。

I also tried using std::transform , but this quickly became very ugly. 我也尝试过使用std::transform ,但这很快变得非常难看。 For non-complex types, I managed to get it working using a typedef: 对于非复杂类型,我设法使用typedef使其工作:

std::vector<float> A(2, -1.23f);
typedef float (*func_ptr)(float);
std::transform(A.begin(), A.end(), A.begin(), (func_ptr) std::abs);

However, attempting the same trick with std::complex<> types caused a run-time crash. 但是,尝试使用std :: complex <>类型的相同技巧会导致运行时崩溃。

Is there a nice way to get this working? 有没有一个很好的方法让这个工作? I have been stuck on this for ages. 多年来我一直坚持这一点。

I still think you should use std::transform : 我仍然认为你应该使用std::transform

template <class OutputIter, class UnaryFunction>
void apply_pointwise(OutputIter first, OutputIter last, UnaryFunction f)
{
    std::transform(first, last, first, f);
}

This function works not only for std::vector types but indeed any container that has a begin() and end() member function, and it even works for C-style arrays with the help of the free functions std::begin and std::end . 这个函数不仅适用于std::vector类型,而且适用于任何具有begin()end()成员函数的容器,它甚至可以在自由函数std::beginstd::end的帮助下用于C风格的数组std::end The unary function may be any free function, a functor object, a lambda expression or even member functions of a class. 一元函数可以是任何自由函数,仿函数对象,lambda表达式或甚至是类的成员函数。

As for the problem with std::sin , this free function is templated and so the compiler cannot know which template instantiation you need. 至于std::sin的问题,这个自由函数是模板化的,因此编译器无法知道您需要哪个模板实例化。

If you have access to C++11, then simply use a lambda expression: 如果您可以访问C ++ 11,那么只需使用lambda表达式:

std::vector<float> v;
// ...
apply_pointwise(v.begin(), v.end(), [](const float f)
{
    return std::sin(f);
});

This way, the compiler knows that it should substitute T=float as the template parameter. 这样,编译器就知道它应该替换T=float作为模板参数。

If you can use C functions, you can also use the function sinf , which is not templated and takes a float as a parameter: 如果你可以使用C函数,你也可以使用函数sinf ,它不是模板化的并且将float作为参数:

apply_pointwise(v.begin(), v.end(), sinf);

You should have a look at this post by Richel Bilderbeek ( Math code snippet to make all elements in a container positive ) which shows you why abs won't work in transform like that. 你应该看一下Richel Bilderbeek发布的这篇文章( 数学代码片段,使容器中的所有元素都为正 ),这样可以告诉你为什么abs不能在变换中起作用。 The reason it won't work is due the abs function structure (see http://www.cplusplus.com/reference/cstdlib/abs/ ). 它无法工作的原因是abs功能结构(参见http://www.cplusplus.com/reference/cstdlib/abs/ )。 You will see abs is not templated itself unlike some other functions (mostly binary functions) found in the functional library. 您将看到abs本身不是模板化的,与functional库中的其他函数(主要是二元函数)不同。 A solution available on Richel's website to show you how you would apply abs to say, a vector of integers. Richel网站上提供的解决方案,向您展示如何应用abs来表示整数向量。

Now if you wanted to apply abs to a container using transform, you should know that the transform function received a complex object and will not know how to apply abs to it. 现在,如果您想使用transform将abs应用于容器,您应该知道转换函数接收到一个复杂的对象,并且不知道如何对它应用abs。 The easiest way to solve this is to simply write your own templated unary functions. 解决这个问题的最简单方法是简单地编写自己的模板化一元函数。

I have an example below where I apply abs to the real and imaginary parts of the complex object. 我在下面有一个例子,我将abs应用于复杂对象的实部和虚部。

#include <iostream>
#include <vector>
#include <algorithm>
#include <complex>
#include <functional>

template <typename T>
std::complex<T> abs(const std::complex<T> &in) {
  return std::complex<T>(std::abs(in.real()), std::abs(in.imag()));
};

int main() {
  std::vector<std::complex<int>> v;
  std::complex<int> c(10,-6);
  v.push_back(c);
  std::complex<int> d(-5, 5);
  v.push_back(d);

  std::transform(v.begin(), v.end(), v.begin(), abs<int>);

  //Print out result
  for (auto it = v.begin(); it != v.end(); ++it)
    std::cout << *it << std::endl;

  return 0;
}

As you have mentioned, you wanted to do apply the abs from the complex library. 正如您所提到的,您希望从complex库中应用abs。 To avoid the unspecified behaviour (see this ) you would use the double typename for the complex objects. 为了避免未指定的行为(请参阅 ),您将对复杂对象使用double typename。

#include <iostream>
#include <vector>
#include <algorithm>
#include <complex>
#include <functional>

int main() {
  std::vector<std::complex<double>> v;
  std::complex<double> c(3, 4);
  v.push_back(c);
  std::complex<double> d(-5, 5);
  v.push_back(d);

  std::transform(v.begin(), v.end(), v.begin(), std::abs<double>); //abs from <complex>

  //Print out result
  for (auto it = v.begin(); it != v.end(); ++it)
    std::cout << *it << std::endl;

  return 0;
}

If you can use valarray instead of vector, then you can do like this 如果你可以使用valarray而不是vector,那么你可以这样做

#include <iostream>
#include <valarray>

int main()
{
valarray<double> dva{0.2, 0.4, 0.6, 0.8,1.0};

// scalar operation   
dva += 0.25; // add 0.25 to each element of valarray inplace

// applying trignometric function
// apply sin function to each element and returns new valarray   
valarray<double> dva2 = std::sin(0.7 * dva);

// apply custom function to each 
// element. returns new valarray   
valarray<double> dva3 = dva.apply([](double dval) {
return (dva * dva)/2;
});

return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM