我正在努力改进我的C++编码,我想问一下以下问题的最有效、最优雅的解决方案是什么:
我实现了一个具有两个成员函数的类。他们都是替身。除了一个内部调用之外,这些函数的主体是相同的。在其中一个中,我想调用幂函数pow(x[idx], Moment)
。在另一个例子中,我想调用不同类的另一个对象的另一成员函数。
#include <vector>
#include <cmath>
class Pot {
public:
Pot() {}
virtual double GetPot(const double x) const {return sin(x);}
virtual ~Pot() {}
}
class BSplOper {
private:
const Pot *m_p;
... (other private members)
public:
BSplOper(const Pot *p) : m_p{p} {}
virtual ~BSplOper() {}
void MkMat1();
void MkMat2();
}
void BSplOper::MkMat1() {
double val = 0.0;
for (some nested loops here) {
... (some more code - index transformations, etc)
for (int indx = 0; indx < LargeNumber; indx++) {
... (some more nested loops)
val += pow(x[indx], someconst);
}
}
}
void BSplOper::MkMat2() {
double val = 0.0;
for (the same code as in MkMat1) {
...(the same code as in MkMat1)
for (int indx = 0; indx < LargeNumber; indx++) {
... (the same code as MkMat1)
val += m_p->GetPot(x[indx]);
}
}
}
有没有任何方法可以将其实现为一个单独的函数,该函数将有一个参数来决定将在内部调用哪个函数?问题是,这个函数会被调用很多次,它的性能非常重要。它位于一系列嵌套循环中。因此,我不想把一个条件放在里面。
我想使用std::function
作为该成员函数的参数,该函数将传递对预期函数的引用。但是,我不确定std::function
的开销。
使用模板化的成员函数会更有效吗?类似的东西
template<typename F>
void BSplOper::MkMat(F fnx) {
double val = 0.0;
for (the same code as in MkMat1) {
...(the same code as in MkMat1)
for (int indx = 0; indx < LargeNumber; indx++) {
... (the same code as MkMat1)
val += (*fnx)(x[indx]);
}
}
}
在这种情况下,正确的语法是什么?或者这两种解决方案都是完全错误的?谢谢你的推荐。
使用函数指针的缺点是编译器可能无法进行所有可能的优化,因此您希望在编译时向编译器提供尽可能多的信息。
为此,您可以将该信息作为模板参数而不是函数参数传递。
一种方法是将if constexpr
与模板参数结合使用。
下面的代码只是一个快速而肮脏的示例,说明了如何做到这一点。但您可能希望使用bool
以外的其他内容。
struct BSplOper {
template<bool F>
void MkMat() {
double val = 0.0;
for (some nested loops here) {
... (some more code - index transformations, etc)
for (int indx = 0; indx < LargeNumber; indx++) {
... (some more nested loops)
if constexpr(F) {
val += pow(x[indx], someconst);
} else {
val += m_p->GetPot(x[indx]);
}
}
}
}
void MkMat1() {
MkMat<true>();
}
void MkMat2() {
MkMat<false>();
}
};
就可维护性和语义而言,if constexpr
并不是最好的解决方案。但如何以正确的方式解决这个问题取决于实际的代码和值依赖关系以及提升时间。
但是,我不确定
std::function
的开销
它应该类似于虚拟调用。
在这种情况下,调用它的正确语法是什么?
val += (*fnx)(x[indx]);
应该只是val += fnx(x[indx]);
template <typename F>
void BSplOper::MkMat(F fnx) {
double val = 0.0;
for (the same code as in MkMat1) {
...(the same code as in MkMat1)
for (int indx = 0; indx < LargeNumber; indx++) {
... (the same code as MkMat1)
val += fnx(x[indx]);
}
}
}
呼叫类似
MkMat([&](const auto& elem){ return pow(elem, someconst);});
或者这两种解决方案都是完全错误的?
它们都有优点和缺点。
它们都处理任何函数,而不仅仅是函数指针。
std::function
有运行时开销,但表达了明确的期望- 模板并没有开销,但并没有表现力(duck类型((如果需要,concept(C++20(或SFINAE/static_assert可能在这方面有所帮助(。它是模板,并强制在头中实现可见(除非显式实例化(
一些人提出function_view
(std::function
的非拥有版本,具有std::function
的表现力,没有开销(。它甚至允许将cpp中的函数实现为不再是模板。