简体   繁体   中英

Square bracket [] operator overloading c++

I have a project that wants me to make a BigNum class in c++ (university project) and it said to overload operator bracket for get and set but the problem is if the set was invalid we should throw an exception the invalid is like

BigNum a;
a[i]=11;//it is invalid because its >9

in searching I found out how to make the set work

C++ : Overload bracket operators [] to get and set

but I didn't find out how to manage setting operation in c# you easily can manage the set value what is the equivalent of it in c++

to make it clear in C# we can say

public int this[int key]
{
    set
    {
        if(value<0||value>9)throw new Exception();
        SetValue(key,value);
    }
}

New Answer

I have to rewrite my answer, my old answer is a disaster.

The check should happen during the assignment, when the right hand side ( 11 ) is available. So the operator which you need to overload is operator= . For overloading operator= , you need to make at least one operator being an user defined type. In this case, the only choice is the left hand side.

The left hand side we have here is the expression a[i] , we thus need to declare the return type of the operator[] being called a user defined type, say BigNumberElement . Then declare an operator= for BigNumberElement and do the range check inside the body of operator= .

class BigNum {
  public:
  class BigNumberElement {
    public:
      BigNumberElement &operator=(int rhs) {
        // TODO : range check
        val_ = rhs;
        return *this;
      }

    private:
      int val_ = 0;
  };

  BigNumberElement &operator[](size_t index) {
    return element_[index];
  }

  BigNumberElement element_[10];
};

OLD answer

You can define a wapper, say NumWapper , which wraps a reference of BigNum's element. The operator= of BigNum returns the wrapper by value.

a[i]=11;

is then something like NumWrapper x(...); x = 11 NumWrapper x(...); x = 11 . Now you can do those checks in the operator= of NumWrapper .


class BigNum {
 public:
  NumWrapper operator[](size_t index) {
    return NumWrapper(array_[index]);
  }

  int operator[](size_t index) const {
    return array_[index];
  }
};

In the NumWrapper, overload some operators, such as:

class NumWrapper {
 public:
  NumWrapper(int &x) : ref_(x) {}
  NumWrapper(const NumWrapper &other) : ref_(other.ref_) {}

  NumWrapper &operator=(const NumWrapper &other);
  int operator=(int x);
  operator int();

 private:
  int &ref_;
};

You can also declare the NumWrapper's copy and move constructor as private, and make BigNum his friend, for preventing user code from copying your wrapper. Such code auto x = a[i] will not compile if you do so, while user code can still copy the wrapped value by auto x = static_cast<T>(a[i]) (kind of verbose though).

auto &x = a[i]; // not compiling
const auto &x = a[i]; // dangerous anyway, can't prevent.

Seems we are good.


These is also another approach: store the elements as a user defined class, say BigNumberElement . We now define the class BigNum as :

class BigNum {
 // some code
 private:
  BigNumberElement array_[10];
}

We need to declare a whole set operators for BigNumberElement, such as comparison(can also be done through conversion), assignment, constructor etc. for making it easy to use.

auto x = a[i] will now get a copy of BigNumberElement, which is fine for most cases. Only assigning to it will sometimes throw an exception and introduce some run-time overhead. But we can still write auto x = static_cast<T>(a[i]) (still verbose though...). And as far as I can see, unexpected compile-time error messages is better than unexpected run-time exceptions.

We can also make BigNumberElement non-copyable/moveable... but then it would be the same as the first approach. (If any member functions returns BigNumberElement & , the unexpected run-time exceptions comes back.)

the following defines a type foo::setter which is returned from operator[] and overloads its operator= to assign a value, but throws if the value is not in the allowed range.

class foo
{
  int data[10];
public:
  void set(int index, int value)
  {
    if(value<0 || value>9)
      throw std::runtime_error("foo::set(): value "+std::to_string(value)+" is not valid");
    if(index<0 || index>9)
      throw std::runtime_error("foo::set(): index "+std::to_string(index)+" is not valid");
    data[index] = value;
  }
  struct setter {
    foo &obj;
    size_t index;
    setter&operator=(int value)
    {
      obj.set(index,value);
      return*this;
    }
    setter(foo&o, int i)
    : obj(o), index(i) {}
  };
  int operator[](int index) const // getter
  { return data[index]; }
  setter operator[](int index) // setter
  { return {*this,index}; }
};

If what you are trying to do is overload [] where you can input info like a dict or map like dict[key] = val. The answer is actually pretty simple:

lets say you want to load a std::string as the key, and std::vector as the value. and lets say you have an unordered_map as your underlying structure that you're trying to pass info to

std::unordered_map<std::string, std::vector<double>> myMap;

Inside your own class, you have this definition:

class MyClass{
    private:
        std::unordered_map<std::string, std::vector<double>> myMap;
    public:
        std::vector<double>& operator [] (std::string key) {
        return myMap[key];
    }

}

Now, when you want to load your object, you can simply do this:

int main() {

    std::vector<double> x;
    x.push_back(10.0);
    x.push_back(20.0);
    x.push_back(30.0);
    x.push_back(40.0);
    
    MyClass myClass;
    myClass["hello world"] = x;

    double x = myClass["hello world"][0]; //returns 10.0
}

The overloaded [] returns a reference to where that vector is stored. So, when you call it the first time, it returns the address of where your vector will be stored after assigning it with = x. The second call returns the same address, now returning the vector you had input.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM