在 C++ 中的抽象接口上实现运算符重载

Implementing operator overloading on abstract interface in C++

我正在写一个 class,它是 "mathematical function" 的一个实现。

"mathematical quality function" 可以从一个摘要 class 派生:QualityFunction。它包含一个 mutable double quality 存储函数一旦求值后的标量值,以及一个 eval 方法用于求值函数值并且必须由每个派生的 class 实现,因为是纯虚方法。

#include <vector>
#include <iostream>
#include <cmath>

using std::cout;
using std::cerr;
using std::endl;
using std::sin;
using std::cos;
using std::vector;

class MathFunction
{
protected:
    mutable double quality; // mutable keyword allows to modify quality even in const methods.
    virtual void eval(const vector<double> &x) const = 0;

public:
    virtual ~MathFunction() {}

    double &operator()()
    {
        return quality;
    }

    double &operator()(const vector<double> &x) const
    {
        eval(x);
        return quality;
    }
};

然后每个从 QualityFunction 派生的 class 都必须实现 eval 方法,因为有很多可能的 QualityFunction。两个示例是 SumSinFunction 和 SumCosFunction,它们计算参数的 sincos 的总和:

class SumSinFunction : public MathFunction
{
public:
    SumSinFunction(){};
    ~SumSinFunction() {};

protected:
    void eval(const vector<double> &x) const
    {
        quality = 0;
        for (size_t i=0; i<x.size(); ++i)
            quality += sin(x[i]);
    }
};

class SumCosFunction : public MathFunction
{
public:
    SumCosFunction(){};
    ~SumCosFunction() {};

protected:
    void eval(const vector<double> &x) const
    {
        quality = 0;
        for (size_t i=0; i<x.size(); ++i)
            quality += cos(x[i]);
    }
};

需要此层次结构,因为 class Maximizer 接受 MathFunction 个对象,并且重复调用 eval 将找到解决方案 vector<double> x 最大化综合素质。

class Maximizer
{
public:
    Maximizer(){}
    vector<double>  maximize(const MathFunction &f)
    {
        // do some operations to maximize it
        // and return the maximized value
        return std::vector<double>();
    }
};

现在的问题是当我想通过组合派生对象来创建 MathFunctions 的线性组合以创建一个新对象时,该对象仍然是一个 MathFunction 实例及其成员变量和方法,但它存储了一个质量是其组件质量的线性组合。例如,我想在 MathFunction 上实现 operator+ 重载以允许我创建这样的东西:

SumCosFunction cosfun;
SumSinFunction sinfun;
MathFunction m = cosfun + sinfun;

第一个尝试是通过友元函数

重载 operator+
friend MathFunction& operator+(const MathFunction &f1, const MathFunction &f2)
{
    MathFunction *f;
    // do something
}

但我不能,因为 MathFunction 的构造函数是虚函数!所以问题是,我如何组合从 MathFunction 派生的不同对象来生成一个可以作为 MathFunction 传递给我的 Maximizer class 的对象? 完整代码可在 coliru http://coliru.stacked-crooked.com/a/3c33664066a3658b

int main()
{
    vector<double> x;
    for (int i=0; i<10;i++)
        x.push_back(i);

    SumCosFunction cosfun;
    SumSinFunction sinfun;

    //MathFunction F;// = cosfun+sinfun;

    Maximizer opt;
    opt.maximize(cosfun);

    return 0;
}

您需要 return 一个 MathFunction,其 eval 是根据另外两个 MathFunctioneval 计算的。您没有执行此操作的 class,因此您需要定义一个。

class SumFunction : public MathFunction
{
   MathFunction *one, *two;
  public:
   SumFunction (MathFunction* one, MathFunction* two) 
     : one(one), two(two) {}
   void eval(const vector<double> &x) const
   {
     one->eval();
     two->eval();
     quality = (*one)() + (*two)(); // is this ever correct?
   }
};

作为旁注,我认为您的设计将从使 eval return 成为计算的质量值并丢弃 operator() 和可能的 quality 中受益匪浅。此外,在实际代码中,您可能希望使用 std::shared_ptr 而不是原始指针。

如何实现(示例)pimpl 习语

MathFunctionImpl 将是所有函数的基础

class MathFunctionImpl
{
  protected:
    mutable double quality; // mutable keyword allows to modify quality even in const methods.

    virtual void eval(const vector<double> &x) const = 0;
  public:

    virtual ~MathFunctionImpl() {}

    double &operator()()
    { return quality; }

    double &operator()(const vector<double> &x) const
    {
        eval(x);
        return quality;
    }

    virtual MathFunctionImpl* Clone() const = 0;
};

我们可以使用UnionFunciton来扩展函数之间的操作:

class UnionFunction : public MathFunctionImpl
{
  public:

    UnionFunction( MathFunctionImpl* f1, MathFunctionImpl* f2 )
      : f1(f1), f2(f2)
    { }

    ~UnionFunction()
    { delete f1; delete f2; }

  protected:

    MathFunctionImpl* f1;
    MathFunctionImpl* f2;
};

现在,SumSinFunctionSumCosFunction 需要一些更改。我添加了控制台消息来测试代码

class SumSinFunction : public MathFunctionImpl
{
  public:
    SumSinFunction(){}
    ~SumSinFunction() {}

  protected:
    void eval(const vector<double> &x) const
    {
      quality = 0;
      for (size_t i=0; i<x.size(); ++i)
      {
        if( i>0) std::cout << "+";
        std::cout << "sin(" << x[i] << ")";
        quality += sin(x[i]);
      }
    }

    MathFunctionImpl* Clone() const
    { return new SumSinFunction; }
};

class SumCosFunction : public MathFunctionImpl
{
  public:
    SumCosFunction(){}
    ~SumCosFunction(){}

  protected:
    void eval(const vector<double> &x) const
    {
      quality = 0;
      for (size_t i=0; i<x.size(); ++i)
      {
        if( i>0) std::cout << "+";
        std::cout << "cos(" << x[i] << ")";
        quality += cos(x[i]);
      }
    }

    MathFunctionImpl* Clone() const
    { return new SumCosFunction; }
};

现在 class 添加到函数:

class SumFunctions : public UnionFunction
{
  public:

    SumFunctions(MathFunctionImpl* f1, MathFunctionImpl* f2 )
      : UnionFunction(f1,f2)
    { }

    ~SumFunctions()
    { }

    void eval(const vector<double> &x) const
    {
      std::cout << "(";
      quality = (*f1)(x);
      std::cout << "+";
      quality += (*f2)(x);
      std::cout << ")";
    }

    MathFunctionImpl* Clone() const
    { return new SumFunctions(f1->Clone(),f2->Clone()); }
};

好的,我们需要创建一个 class 来存储我们的 pimpl classes:

class MathFunction
{
  public:

    MathFunction( MathFunctionImpl* impl )
      : impl(impl)
    { }

    ~MathFunction()
    { delete impl; }

    double &operator()()
    {
      return (*impl)();
    }

    double &operator()(const vector<double> &x) const
    {
      return (*impl)(x);
    }

    // This method can be friend
    MathFunction operator+(const MathFunction& f2) const
    {
      return MathFunction(new SumFunctions(impl->Clone(), f2.impl->Clone()));
    }

  private:
    MathFunctionImpl* impl;
};

仅此而已。主要测试代码:

int main()
{
  vector<double> x;
  for (int i=0; i<10;i++)
    x.push_back(i);

  MathFunction f1( new SumCosFunction );
  MathFunction f2( new SumSinFunction );
  MathFunction sum = f1 + f2;

  double value = sum(x);
  std::cout << "=" << value << std::endl;
  return 0;
}

我会选择基于 std::function 的不同设计。

typedef std::function<double (std::vector<double> const&)> MathFunction;

MathFunction sum(MathFunction f1, MathFunction f2)
{
     return [f1,f2](std::vector<double> const& x) {
          return f1(x) + f2(x); 
     };
}

您可以使用模板方法或策略设计模式来简化单个函数的定义,或者编写一个使用 std::function<double (double)> 的通用函数,或者使用模板参数。我会坚持你原来的例子。

class SumCosFunction
{
public:

    double operator()(const vector<double> &x) const
    {
        if (!quality) {
            *quality = 0;
            for (size_t i=0; i<x.size(); ++i)
                *quality += cos(x[i]);
        }
        return *quality;
    }

private:
    mutable std::experimental::optional<double> quality;
};

class SumSinFunction
{
public:

    double operator()(const vector<double> &x) const
    {
        if (!quality) {
            *quality = 0;
            for (size_t i=0; i<x.size(); ++i)
                *quality += sin(x[i]);
        }
        return *quality;
    }

private:
    mutable std::experimental::optional<double> quality;
};


Combining both functions is simple:

auto consSin = sum( SumCosFunction{}, SumSinFunction{} );