如何将函数应用于数组中的所有元素(在 C++ 模板中 class)
How to apply function to all elements in array (in C++ template class)
我有一个存储数字数组的模板 class,我想将现有(标量)函数应用于每个元素。例如,如果我们假设我的 class 是 std::vector,那么我希望能够在所有元素上调用(例如)std::cos 函数。
也许电话会是这样的:
std::vector<float> A(3, 0.1f);
std::vector<float> B = vector_function(std::cos, A);
N.B。我还必须处理 std::complex<> 类型(为此调用了适当的复杂 std::cos 函数)。
我发现 this answer 建议将函数类型作为模板参数:
template<typename T, typename F>
std::vector<T> vector_function(F func, std::vector<T> x)
但是,我根本无法让它工作(可能是因为像 std::sin 和 std::cos 这样的函数都是模板化和重载的?)。
我也尝试过使用 std::transform
,但这很快就变得非常难看。对于非复杂类型,我设法使用 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);
但是,尝试对 std::complex<> 类型使用相同的技巧会导致 运行 次崩溃。
有什么好的方法可以让它工作吗?我多年来一直坚持这个。
您应该看看 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. The reason it won't work is due the abs function structure (see http://www.cplusplus.com/reference/cstdlib/abs/) 的 post。您会看到 abs
本身并没有模板化,这与 functional
库中的一些其他函数(主要是二元函数)不同。
Richel 网站上提供的解决方案向您展示了如何应用 abs 表示整数向量。
现在如果你想使用 transform 将 abs 应用到容器,你应该知道 transform 函数接收到一个复杂的对象并且不知道如何将 abs 应用到它。解决这个问题的最简单方法是简单地编写您自己的模板化一元函数。
我在下面有一个示例,其中我将 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;
}
如您所述,您想应用 complex
库中的 abs。为避免未指定的行为(参见 this),您将为复杂对象使用 double
类型名称。
#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;
}
我仍然认为你应该使用 std::transform
:
template <class OutputIter, class UnaryFunction>
void apply_pointwise(OutputIter first, OutputIter last, UnaryFunction f)
{
std::transform(first, last, first, f);
}
此函数不仅适用于 std::vector
类型,而且实际上适用于任何具有 begin()
和 end()
成员函数的容器,它甚至适用于 C 风格的数组自由函数 std::begin
和 std::end
。一元函数可以是任何自由函数、仿函数对象、lambda 表达式甚至 class.
的成员函数
至于 std::sin
的问题,这个自由函数是模板化的,因此编译器无法知道您需要哪个模板实例化。
如果您可以使用 C++11,那么只需使用 lambda 表达式即可:
std::vector<float> v;
// ...
apply_pointwise(v.begin(), v.end(), [](const float f)
{
return std::sin(f);
});
这样,编译器就知道它应该将 T=float
替换为模板参数。
如果你可以使用 C 函数,你也可以使用函数 sinf
,它没有模板化并且以一个 float
作为参数:
apply_pointwise(v.begin(), v.end(), sinf);
如果你可以用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;
}
我有一个存储数字数组的模板 class,我想将现有(标量)函数应用于每个元素。例如,如果我们假设我的 class 是 std::vector,那么我希望能够在所有元素上调用(例如)std::cos 函数。
也许电话会是这样的:
std::vector<float> A(3, 0.1f);
std::vector<float> B = vector_function(std::cos, A);
N.B。我还必须处理 std::complex<> 类型(为此调用了适当的复杂 std::cos 函数)。
我发现 this answer 建议将函数类型作为模板参数:
template<typename T, typename F>
std::vector<T> vector_function(F func, std::vector<T> x)
但是,我根本无法让它工作(可能是因为像 std::sin 和 std::cos 这样的函数都是模板化和重载的?)。
我也尝试过使用 std::transform
,但这很快就变得非常难看。对于非复杂类型,我设法使用 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);
但是,尝试对 std::complex<> 类型使用相同的技巧会导致 运行 次崩溃。
有什么好的方法可以让它工作吗?我多年来一直坚持这个。
您应该看看 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. The reason it won't work is due the abs function structure (see http://www.cplusplus.com/reference/cstdlib/abs/) 的 post。您会看到 abs
本身并没有模板化,这与 functional
库中的一些其他函数(主要是二元函数)不同。
Richel 网站上提供的解决方案向您展示了如何应用 abs 表示整数向量。
现在如果你想使用 transform 将 abs 应用到容器,你应该知道 transform 函数接收到一个复杂的对象并且不知道如何将 abs 应用到它。解决这个问题的最简单方法是简单地编写您自己的模板化一元函数。
我在下面有一个示例,其中我将 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;
}
如您所述,您想应用 complex
库中的 abs。为避免未指定的行为(参见 this),您将为复杂对象使用 double
类型名称。
#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;
}
我仍然认为你应该使用 std::transform
:
template <class OutputIter, class UnaryFunction>
void apply_pointwise(OutputIter first, OutputIter last, UnaryFunction f)
{
std::transform(first, last, first, f);
}
此函数不仅适用于 std::vector
类型,而且实际上适用于任何具有 begin()
和 end()
成员函数的容器,它甚至适用于 C 风格的数组自由函数 std::begin
和 std::end
。一元函数可以是任何自由函数、仿函数对象、lambda 表达式甚至 class.
至于 std::sin
的问题,这个自由函数是模板化的,因此编译器无法知道您需要哪个模板实例化。
如果您可以使用 C++11,那么只需使用 lambda 表达式即可:
std::vector<float> v;
// ...
apply_pointwise(v.begin(), v.end(), [](const float f)
{
return std::sin(f);
});
这样,编译器就知道它应该将 T=float
替换为模板参数。
如果你可以使用 C 函数,你也可以使用函数 sinf
,它没有模板化并且以一个 float
作为参数:
apply_pointwise(v.begin(), v.end(), sinf);
如果你可以用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;
}