模板多项式类的重载运算符 []

Overloading operator[] for a template Polynom class

本文关键字:运算符 重载 多项式      更新时间:2023-10-16

我正在Polynom<T>类编写一个模板,其中T是其系数的数字类型。

多项式的系数存储在一个std::vector<T> coefficients中,其中coefficients[i]对应于实多项式中的x^i。(所以 X 的幂是按递增顺序排列的(。

保证coefficients向量始终包含至少一个元素。 - 对于零多项式,它是 T() .

我想重载operator[]执行以下操作:

  1. 传递给运算符 [] 的索引对应于我们要修改/读取其系数的 X 的幂。
  2. 如果用户只想读取系数,它应该抛出负索引,返回coefficients.at(i)存储范围内的索引 - 并合理地为所有其他索引返回 0,而不是抛出。
  3. 如果用户想要修改系数,它应该抛出负索引,但允许用户自由修改所有其他索引,即使指定的索引大于或等于coefficients.size()。所以我们想以某种方式调整向量的大小。

我遇到的主要问题如下:

1.

如何区分读写和写写?有一个人没有解释就离开了我,但说写了两个版本:

const T& operator[] (int index) const;
T& operator[] (int index);

是不够的。但是,我认为编译器会更喜欢读取情况下的 const 版本,不是吗?

阿拉伯数字。

我想确保coefficients向量中没有尾随零。所以我必须以某种方式提前知道,"之前"我返回系数的可变T&,用户想要分配什么值。我知道operator[]不会收到第二个论点。

显然,如果这个值不为零(不是 T(((,那么我必须调整向量的大小,并将适当的系数设置为传递的值。

但是我不能提前这样做(在从operator[]返回T&之前(,因为如果要分配的值是T((,那么,如果我提前调整系数向量的大小,它最终会有很多尾随的"零"。

当然,我可以检查类的所有其他函数中的尾随零,并在这种情况下删除它们。对我来说似乎是一个非常奇怪的决定,我希望每个函数都开始工作,假设如果向量的大小> 1,则向量末尾没有零。

你能告诉我尽可能具体的解决方案吗?我听说过一些关于编写一个隐式转换为带有重载operator= T&的内部类,但我缺乏细节。

提前非常感谢!

您可以

尝试的一个选项(我还没有测试过(:

template<typename T>
class MyRef{
private:
   int index;
   Polynom<T>*p;
public:
    MyRef(int index, Polynom<T>*p) : index(index), p(p) { }
    MyRef<T>& operator=(T const&t); //and define these appropriately
    T operator T() const;         
};

并定义:

    MyRef<T> operator[](int index){
        return MyRef<T>(index, this);
    }

这样,当您为"引用"赋值时,它应该可以访问多项式中所有需要的数据,并采取适当的操作。

我对你的实现不够熟悉,所以我将给出一个非常简单的动态数组的例子,其工作原理如下:

  • 您可以毫无顾虑地从任何int index读取; 以前未写入的元素应读出0 ;
  • 当您写入超过当前分配数组末尾的元素时,该元素将被重新分配,新分配的元素将初始化为 0
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename T>
class my_array{
private:
    T* _data;
    int _size;
    class my_ref{
        private:
            int index;
            T*& obj;
            int&size;
        public:
            my_ref(T*& obj, int&size, int index)
                : index(index), obj(obj), size(size){}
            my_ref& operator=(T const& t){
                if (index>=size){    
                    obj = (T*)realloc(obj, sizeof(T)*(index+1) );
                    while (size<=index)
                        obj[size++]=0;
                }
                obj[index] = t;
                return *this;
            }
            //edit:this one should allow writing, say, v[1]=v[2]=v[3]=4;
            my_ref& operator=(const my_ref&r){              
                operator=( (T) r);
                return *this;
            }
            operator T() const{
                return (index>=size)?0:obj[index];
            }
    };
public:
    my_array() : _data(NULL), _size(0) {}
    my_ref operator[](int index){
        return my_ref(_data,_size,index);
    }
    int size() const{ return _size; }
};
int main(){
    my_array<int> v;
    v[0] = 42;
    v[1] = 51;
    v[5] = 5; v[5]=6;
    v[30] = 18;
    v[2] = v[1]+v[5];
    v[4] = v[8]+v[1048576]+v[5]+1000;
    cout << "allocated elements: " <<  v.size() << endl;
    for (int i=0;i<31;i++)
        cout << v[i] << " " << endl;
    return 0;
}

这是一个非常简单的例子,以目前的形式不是很有效,但它应该证明这一点。

最终,您可能希望重载operator&以允许*(&v[0] + 5) = 42;之类的功能正常工作。对于此示例,您可以让该operator&给出一个my_pointer,该定义operator+在其index字段上进行算术运算并返回新my_pointer。最后,您可以重载operator*()以返回到my_ref

解决方案是代理类(未经测试的代码如下(:

template<typename T> class Polynom
{
public:
   class IndexProxy;
   friend class IndexProxy;
   IndexProxy operator[](int);
   T operator[](int) const;
   // ...
private:
   std::vector<T> coefficients;
};
template<typename T> class Polynom<T>::IndexProxy
{
public:
  friend class Polynom<T>;
  // contrary to convention this assignment does not return an lvalue,
  // in order to be able to avoid extending the vector on assignment of 0.0
  T operator=(T const& t)
  {
    if (theIndex >= thePolynom.coefficients.size())
      thePolynom.coefficients.resize(theIndex+1);
    thePolynom.coefficients[theIndex] = t;
    // the assignment might have made the polynom shorter
    // by assigning 0 to the top-most coefficient
    while (thePolynom.coefficients.back() == T())
      thePolynom.coefficients.pop_back();
    return t;
  }
  operator T() const
  {
    if (theIndex >= thePolynom.coefficients.size())
      return 0;
    return thePolynom.coefficients[theIndex];
  }
private:
  IndexProxy(Polynom<T>& p, int i): thePolynom(p), theIndex(i) {}
  Polynom<T>& thePolynom;
  int theIndex;
}
template<typename T>
  Polynom<T>::IndexProxy operator[](int i)
  {
    if (i < 0) throw whatever;
    return IndexProxy(*this, i);
  }
template<typename T>
  T operator[](int i)
{
  if (i<0) throw whatever;
  if (i >= coefficients.size()) return T();
  return coefficients[i];
}

显然上面的代码没有优化(尤其是赋值运算符显然有优化的空间(。

您无法区分使用运算符重载的读取和写入。 您能做的最好的事情是区分const设置和非const设置中的用法,这是您的代码片段所做的。 所以:

Polynomial &poly = ...;
poly[i] = 10;  // Calls non-const version
int x = poly[i];  // Calls non-const version
const Polynomial &poly = ...;
poly[i] = 10;   // Compiler error!
int x = poly[i]  // Calls const version

因此,听起来您的两个问题的答案都是具有单独的setget功能。

我看到您的问题的两种解决方案:

  1. 与其将系数存储在std::vector<T>不如将它们存储在std::map<unsigned int, T>中。这样,您将永远只存储非零系数。您可以创建自己的基于 std::map 的容器,该容器将使用存储在其中的零。这样,您还可以为具有大 n 的 x^n 形式的多项式节省一些存储空间。

  2. 添加一个将存储索引(幂(和系数值的内部类。您将从 operator[] 返回对此内部类的实例的引用。内部类将覆盖operator= 。在被覆盖的operator=中,您将获取存储在内部类实例中的索引(幂(和系数,并将它们刷新到存储系数的std::vector

是不可能的。我能想到的唯一方法是提供一个特殊的成员函数来添加新系数。

编译器通过查看Polynom的类型来决定const版本和非const版本,而不是通过检查对返回值执行的操作类型。