C++ - 两个成员函数因单个函数调用而不同

C++ - Two member functions differ by a single function call

我正在努力改进我的 C++ 编码,我想问问以下问题的最有效和最优雅的解决方案是什么:

我用两个成员函数实现了一个class。它们都填充了双打数组。这些函数的主体除了内部深处的一个调用外是相同的。在其中一个中,我想调用幂函数 pow(x[idx], Moment)。另一方面,我想调用另一个对象的另一个成员函数 class.

#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 不是可维护性和语义方面的最佳解决方案。但是如何以正确的方式解决这个问题取决于实际的代码和值的依赖性和liftimes。

However, I am not sure about the overhead of std::function

应该类似于虚拟调用。

In that case, what would be the proper syntax to call it?

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);});

Or are both these solutions entirely wrong?

它们各有利弊。

它们都处理任何仿函数而不仅仅是函数指针。

  • std::function 有运行时开销,但明确表示期望。
  • 模板没有开销,但没有表现力(鸭子打字)(如果需要,概念 (C++20) 或 SFINAE /static_assert 可能在这方面有所帮助)。它是模板并在 header 中强制可见实现(除非显式实例化)。

有人提议 function_viewstd::function 的非拥有版本,具有 std::function 的表现力,没有开销)。 它甚至允许在 cpp 中实现该功能,不再是模板。