5

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(); } 
2
  • 4
    The sparse description of your problem suggests to me that you would benefit from a redesign. I think a much better way to start this problem is to write a set method and write a get method, and then ask the question about how to combine them so that you can do both through operator[]. Commented Jun 15, 2015 at 7:42
  • 2
    This looks similar to a more common request, having operator[ ] throw when attempting to read a missing value from a collection, and still allowing insertion of a new value. Commented Jun 15, 2015 at 8:34

6 Answers 6

7

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.
Sign up to request clarification or add additional context in comments.

Comments

4

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.

Comments

4

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.

Comments

1

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.

Comments

-1

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.

1 Comment

I think this is being down-voted because it doesn't deal with one of the main parts of the question: 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.
-1

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.)

3 Comments

The "Get" version is not necessarily the const-version. It could be non-const version as well, if the object is non-const.
@Bathsheba: It's not a cmplication, it's the answer.
Attempted to salvage some dignity. If it's still judged to be useless or a better answer emerges; I'll tin in.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.