chen
chen

Reputation: 53

C++ operator []

I am trying to implement the operator [] that is to be used once for Set and once as Get, i need to differentiate between the two cases, as in the case of get, i need to throw an exception if the returned value is equal to -1; whereas in the case of Set i just overwrite the value. appple[2] = X; y=apple[2];

I dont know how to differentiate between the two modes, my function signature is:

   double& Security::operator[](QuarterType index){
    if(index<0 || index>MAX_QUATERS){
        throw ListExceptions::QuarterOutOfBound();
    }
    return evaluation[index].getValueAddress();
   }

Upvotes: 5

Views: 612

Answers (6)

peterchen
peterchen

Reputation: 41096

C++ does not easily allow distinction of

appple[2] = X; 
y=apple[2];

The most common solution is to return a proxy object:

struct AppleProxy
{
   Security & obj; 
   unsigned index;

   AppleProxy(Security &, unsigned) : ... {}

   operator double()   // your getter
   {
      return obj.GetAt(index);
   }

   operator=(double rhs)
   {
      obj.SetAt(index, rhs);
   }
}

AppleProxy operator[](unsigned index) { return AppleProxy(*this, index); }

Proxy require significant attention to detail, I've omitted const correctness, life-time management, taking-the-address-of (double & x = apple[2]; x = 17;), etc.

Due to the pitfalls, I tend to avoid them.

Upvotes: 4

user1084944
user1084944

Reputation:

Unlike languages such as python or C#, C++ doesn't offer get and set modes for its operators. Thus, you have to write operator[] so that it returns an object that does the right thing when used in any context you want it to be used, which usually means a reference.

Note I said usually; there are other things you can do, such as return an instance of a class with the following members:

struct proxy
{
    operator double() const;
    void operator=(double x);
};

If this proxy object is used in a context expecting a double, it will invoke the implicit conversion operator, which is implemented to do your get operation. Similarly, if someone assigns to the proxy object, it will invoke the assignment operator, which is implemented to do your set operation.

(note that the proxy will probably need a reference to your Security object)

This approach is not without its difficulties, though; since your return value is not of type double or double& or similar, it can cause confusion with overload resolution, especially with template functions, and cause a lot of confusion when used with auto. And your proxy object can't even be used in a context that needs a double&!

Thus, this is something of a last resort, when you're backed into a corner and have to do access in terms of an operator[] that pretends to be working with double. Before you resort to this, you really should seek other options, such as:

  • Redesign your program so that indexing Security objects can just return references to doubles rather than needing more complicated get and set operations. (e.g. change the invariants so there isn't exceptional data, or make the error handling happen somewhere else)
  • Settle for using dedicated get and set operations. Make even give them more sophisticated names to reflect the fact that they are doing something rather more nontrivial than one normally thinks of indexing collections.
  • Redesign the API so that the user is expecting to obtain a proxy object from operator[] that has various nontrivial behaviors, rather than expecting to use operator[] to read/write double objects.

Upvotes: 7

Alexander Balabin
Alexander Balabin

Reputation: 2075

You can return a proxy object from your [] operator that has an implicit conversion operator double for the target type and operator =. If the conversation operator is called and your value is -1 you throw, if the assignment is called you assign.

Upvotes: 1

utnapistim
utnapistim

Reputation: 27365

I am trying to implement the operator [] that is to be used once for Set and once as Get

It cannot be done. You can implement multiple versions of the operator, with different signatures (that is, with const and non-const versions) but they will not be called correctly.

Instead, consider implementing set and get functions with the functionality you require.

Upvotes: 4

Bathsheba
Bathsheba

Reputation: 234645

The "Get" version could take a const reference and be marked as const:

const double& Security::operator[](QuarterType index) const

The "Set" version could be written as you currently have it:

double& Security::operator[](QuarterType index)

At times when you need to force the const version to be called, you can const_cast your object to a const type. (Don't do it the other way round though: the behaviour of casting away const-ness is undefined if the original object was const).

But personally I see this as unnecessarily complicated. It might be more practical to move away from operator overloading and write separate get and set functions instead. You could do something with the QuarterType class: perhaps imbuing a traits system into it which allows the function to deal with specific QuarterType instances accordingly. But that's probably no better than having separate functions with different names.

(If QuarterType is a large object, then consider passing const QuarterType&. That will prevent a value copy from being taken.)

Upvotes: -1

eonj
eonj

Reputation: 119

Look over operator overloading article on cppreference.com. The Array subscript operator section explains what you need exactly:

User-defined classes that provide array-like access that allows both reading and writing typically define two overloads for operator[]: const and non-const variants:

struct T {
          value_t& operator[](std::size_t idx)       { return mVector[idx]; };
    const value_t& operator[](std::size_t idx) const { return mVector[idx]; };
};

If the value type is known to be a built-in type, the const variant should return by value.

(continued)

So, you should define double Security::operator[](QuarterType index) const, instead of const double& Security::operator[](QuarterType index) const.

Upvotes: -1

Related Questions