我有一个对数据进行一些计算的类。在这种情况下,将进行转换。但是,该类不在乎转换计算是什么或它需要的参数(它只知道必须在整数向量上完成)。 compute()成员函数进行所有计算。
class Transform {
public:
Transform();
void compute();
void print();
private:
std::vector<int> data;
};
在较旧的C ++中,我猜想应该做的正确的事情是从Transform类继承(并使计算虚拟化),并使用其特定参数实现所有自定义计算功能并重载它们。
class TransformType1 : public Transform {
public:
Transform();
void compute();
void compute(int max_int, int min_int); //Truncates all the values outside of range
void print();
private:
std::vector<int> data;
};
使用lambda和给定向量的std::transform
可以很好地做到这一点,那么,为什么不在类内部使用相同的方法呢?
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
// Generic class, used for other stuff does not care about the specifics
// of the parameters used in the compute lambda
class Transform {
public:
Transform();
void compute();
// This std::function will be set though the constructor in the future and
// will be private
std::function<int(int)> t_lambda;
void print();
private:
std::vector<int> data;
};
Transform::Transform() : data({1, 3, 4, 5, 2, 1, 4, 5, 3, 5, 3, 6, 4, 6, 12}) {}
// Apply the transform given the function object
void Transform::compute() {
std::transform(data.begin(), data.end(), data.begin(), t_lambda);
}
void Transform::print() {
for (auto& e : data) {
std::cout << e << " ";
}
std::cout << std::endl;
}
int main() {
Transform f;
int max_num = 2; // External parameters
// The important part: lambda parameters are taken with the capture feature
f.t_lambda = std::function<int(int)>([max_num](int x) -> int {
// Truncate values above a threshold
return x > max_num ? max_num : x;
});
f.compute();
f.print();
}
现在,我可以简单地在类外部定义计算函数规则(按预期方式),并且可以保持相同的类型(无继承模式),甚至可以向lambda函数添加更多参数(而不是常量),而无需重载compute()
过去我没有看过很多这样的代码,所以我不确定这是否是一个好主意。在这种情况下,我还可以使用其他哪些模式?
答案 0 :(得分:1)
我认为这不是一个好主意。我在这里看到的主要问题是,std::function
的使用几乎肯定会阻止内联,这对于std ::算法的性能来说是非常重要的。将your approach与this进行比较。第一个示例基本上是上面代码的精简版本。在第二个版本中,我直接将lambda传递给Transform::compute
,而不是通过std::function
。如果查看生成的代码,您将看到在第二个版本中,编译器基本上可以内联所有内容。另一方面,在第一个版本中,编译器无法内联任何内容。计算函数的每次迭代都将间接调用std::function
中包含的lambda。
除了这些性能问题外,我不清楚此类Transform
类到底代表什么。它将输入数据容器与std::function
和print()
方法捆绑在一起,这给我带来了不相关功能的相当随意的聚集。从概念上讲,我认为转换可以应用于某些输入数据以生成某些输出数据。应用于变换的数据似乎与变换本身无关。将向量打印到std::cout
的行为更加如此。在类中封装某种类型的转换没有错。但是我要说的是,您然后希望该Transform
类的实例表示一个特定的转换,然后可以将其应用于任何给定的输入数据。 For example:
#include <algorithm>
#include <vector>
class MyTransform
{
int max_num;
public:
MyTransform(int max_num = 2) : max_num(max_num) {}
void operator ()(std::vector<int>& data) const
{
std::transform(std::begin(data), std::end(data), std::begin(data), [this](int x)
{
return x > max_num ? max_num : x;
});
}
};
int main() {
std::vector<int> data = {1, 3, 4, 5, 2, 1, 4, 5, 3, 5, 3, 6, 4, 6, 12};
MyTransform f(2);
f(data);
}
在这里,MyTransform
实际上代表示例转换的一个实例(对于某些参数max_num
)。 MyTransform
个对象可以传递并应用于任何给定的std::vector<int>
。或the shorthand version:
#include <algorithm>
#include <vector>
auto my_transform(int max_num)
{
return [max_num](auto&& data)
{
std::transform(std::begin(data), std::end(data), std::begin(data), [max_num](int x)
{
return x > max_num ? max_num : x;
});
};
}
int main() {
std::vector<int> data = {1, 3, 4, 5, 2, 1, 4, 5, 3, 5, 3, 6, 4, 6, 12};
auto f = my_transform(2);
f(data);
}
答案 1 :(得分:0)
从有效性和简便性的角度来看,是的-这是一种做事并清除许多原本必须编写的多态代码的完美有效方法(正如您在C ++的较早版本中所提到的)。它非常安全,用途广泛,并且具有广泛的标准化界面。
但是,从性能的角度来看,std::function
被认为是不好的,因为它是一个多态适配器,并且具有与此相关的所有开销。因此,如果要最大速度,则需要使用常规的,名称空间级别或静态的成员函数。在这种情况下,您可以考虑使用void*
参数包习惯用法。
但是请记住,只有在您的程序已经运行并且只想对其速度进行优化时,这才可以实现:
// holds extra args for do_something_1()
struct pack_1
{
int something;
double something_else;
};
// holds extra args for do_something_2()
struct pack_2
{
float cool_stuff;
std::string another_extra_param;
};
// expects _extra to be of type pack_1
void do_something_1(int a, int b, void *_extra)
{
pack_1 *extra = (pack_1*)_extra;
...
}
// expects _extra to be of type pack_2
void do_something_2(int a, int b, void *_extra)
{
pack_2 *extra = (pack_2*)_extra;
...
}
然后,您可以使用纯指针:void (*)(int, int, void*)
用于使用的函数,void*
用于传递的额外参数。您只需要小心并确保将正确的附加参数包类型传递给它即可。