0

I should get this by now, but I'm just not getting it yet. The trouble is operator='s argument could be non-const, but that breaks std::vector::push_back because it makes the item const, so operator= has to accept a const object. Well, I'm not certain on how I'm supposed to modify the this object working like this.

#include <vector> #include <map> #include <iostream> using namespace std; int font[] = {0, 31, 0, 31, 0, 31, 0, 31}; class Foo { int size_; std::map<int, int> chars_; public: Foo(int *font, int size); unsigned int Size() const { return size_; } void Add(int ch); bool operator==(const Foo &rhv) const; int &operator[](int i); int const operator[](int i); Foo operator=(const Foo &rhv); }; Foo::Foo(int *font, int size) { for(int i = 0; i < size; i++ ) { chars_[size_++] = font[i]; } } bool Foo::operator==(const Foo &rhv) const { if(Size() != rhv.Size()) return false; /*for(int i = 0; i < Size(); i++ ) { if ( chars_[i] != *rhv[i] ) return false; }*/ return true; } int &Foo::operator[](int i) { return chars_[i]; } int const Foo::operator[](int i) { return chars_[i]; } Foo Foo::operator=(const Foo &rhv) { if( this == &rhv ) return *this; for(unsigned int i = 0; i < rhv.Size(); i++ ) { //Add(*rhv[i]); //chars_[size_++] = rhv[i]; } return *this; } void Foo::Add(int ch) { chars_[size_++] = ch; } int main() { vector<Foo> baz; Foo bar = Foo(font, 8); baz.push_back(bar); } 

Edit: Well, I've spent some time reading about const again. Is what I want to do even possible? The reason I ask is because of this sentence: If it doesn't compile without const qualifier and you are returning a reference or pointer to something that might be part of the object, then you have a bad design.

I took that into account, and refrained from returning a reference in the const method. That yielded this error:

test.cpp:18: error: 'const int Foo::operator[](int)' cannot be overloaded test.cpp:17: error: with 'int& Foo::operator[](int)' test.cpp:41: error: prototype for 'const int Foo::operator[](int)' does not match any in class 'Foo' test.cpp:37: error: candidate is: int& Foo::operator[](int) 

Getting rid of the int & Foo::operator[] gets rid of that error. I know I can just make a new accessor to apply changes to chars_, but I thought I'd update this and find out if what I'm trying to do is possible at all.

5 Answers 5

5

Your operator[] is unconventional. In your assignment operator, why not just access rhv.chars_ directly?

E.g.

Foo& Foo::operator=(const Foo &rhv) { _size = rhv._size; _chars = rhv._chars; return *this; } 
Sign up to request clarification or add additional context in comments.

Comments

3

The first problem is the syntax of:

int const operator[](int i); 

Should be:

int operator[](int i) const; 

However, fixing that fails, because std::map has no const operator[]. Why is that one may ask? Because it has a side-effect:

Returns a reference to the object that is associated with a particular key. If the map does not already contain such an object, operator[] inserts the default object data_type(). ... Since operator[] might insert a new element into the map, it can't possibly be a const member function. Note that the definition of operator[] is extremely simple: m[k] is equivalent to (*((m.insert(value_type(k, data_type()))).first)).second. Strictly speaking, this member function is unnecessary: it exists only for convenience.

Taken from http://www.sgi.com/tech/stl/Map.html. So instead of operator[] you will want to use the find function.

By the way, for this kind of experimentation and study, I find it convenient to write all class functions inline in the class declaration. Simply, because it faster to modify the class definition, though some C++ purists describe this as bad style.

[EDIT: Here's a full solution]

class Foo { size_t size_; std::map<int, int> chars_; public: Foo(int *font, size_t size) : size_(size) { // size should be of type "size_t" for consistency with standard library // in the original example "unsigned int" and "int" was mixed throughout for (size_t i=0; i < size; ++i) // Reuse the add function. Add(font[i]); } size_t Size() const { return size_; } void Add(int ch) { chars_[size_++] = ch; } bool operator==(const Foo &rhv) const { if (&rhv == this) return true; if (rhv.Size() != size_) return false; for (size_t i=0; i < size_; ++i) if (rhv[i] != (*this)[i]) return false; return true; } int& operator[](size_t i) { assert(i < size_); return chars_.find(i)->second; } const int& operator[](size_t i) const { assert(i < size_); return chars_.find(i)->second; } Foo& operator=(const Foo &rhv) { size_ = rhv.size_; chars_ = rhv.chars_; return *this; } }; int main() { std::vector<Foo> baz; Foo bar = Foo(font, 8); baz.push_back(bar); } 

4 Comments

int operator[](int i) const; wouldn't work. I found a solution that's workable. 'map<int, int> Foo::Chars() const;' and I can just index that return
I did explain why just changing that one line alone won't work. I added a full code example for you. If you are really interested in understanding what is going wrong, I strongly suggest you read the link I pointed you to.
I knew I needed to return a const. At any rate, I had my share of const today. Bonus: I learned what & does. Goodbye to pointers.
Btw, I do read -- a lot. I just learn better by doing. Reading alone doesn't do it for me, but it plants some seeds that sprout when I get my hands dirty.
3

You need to make two different versions of operator[], one of them non-const like you have now and the other one const that returns a const int * instead of int *. That way you can use the operator in both const and non-const contexts.

By the way, why are you returning a pointer instead of a reference from operator[]? From what I've seen, it's more customary to return a reference.

Comments

1

You don't need an assignment operator at all. The auto-generated operator=, which runs each data member's operator=, should be fine for your 2 data member types (int and std::map).

2 Comments

Man, I was getting errors last week that had to do with assignment operators. Today I just assumed I needed one for this new class I added. I never tried it without. Ok, make that 3 things I learned today. Hell, if I could learn 3x the normal per each day for every day, I'd be doing pretty swell. What's a scenario requiring an assignment operator?
You need to define an assignment operator only when the default one isn't appropriate - for example if you have a pointer data member that you allocated in the constructor. Remember The Rule of Three: ddj.com/cpp/184401400 - if you have one of (copy constructor, assignment operator, destructor) there's a good chance you need all three.
0

You need

class Foo { //... int &operator[](int i); int operator[](int i) const; //... }; 

and

int &Foo::operator[](int i) { return chars_[i]; } int Foo::operator[](int i) const { return chars_[i]; } 

i.e. the const goes after the parameter list, not with the return type.

4 Comments

@Dave: Note that the return type can be const, too. It doesn't make much sense for a built-in, since rvalues of built-in type cannot be modified anyway, but it's not really "wrong".
@Scott: What do you mean "it doesn't work"? This is the idiomatic and canonical form of overloading operator[], so if this doesn't work, you'd better find out why and fix that.
Sbi: It's "wrong" in that then it doesn't mean what we want it to mean; it means something different. It wouldn't be wrong if we wanted that other meaning, but we don't.
@Dave: It was another disqualifier error. I found just avoiding accessing the operator[] all together when under the const umbrella solved my problem -- by using an accessor to retrieve chars_.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.