tags:

views:

182

answers:

5

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.

+3  A: 

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.

jk
Makes sense. About the pointer -- it's what came to mind.
Scott
operator= should perhaps return a reference too...
leander
+4  A: 

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;
}
Charles Bailey
A: 

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.

Dave Hinton
That doesn't work either. I'll just stick with an accessor.
Scott
@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".
sbi
@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
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 Hinton
@Scott: What error messages are you getting now?
Dave Hinton
@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_.
Scott
+3  A: 

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);    
}
cdiggins
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
Scott
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.
cdiggins
Scott
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.
Scott
+1  A: 

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

orip
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?
Scott
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: http://www.ddj.com/cpp/184401400 - if you have one of (copy constructor, assignment operator, destructor) there's a good chance you need all three.
orip