Vector and []-operator overloading

2019-01-28 22:04发布

问题:

I have inherited my class from std::vector. Now I want to overload the []-operator.
When I try to assign a new value to my vector, e.g. v[0]=5, I should receive the message OK.

This is my code (I know, that makes no sense, I'm just playing around):

#include<vector>
#include<iostream>
class Vec : public std::vector<int> {
public:
    int operator[](int);
};

int Vec::operator[](int i) {
    (*this)[i] = i;
    std::cout << "OK";
    return 123;
}

int main() {
    Vec v;
    v[0]=5;
}

Unfortunately I get the following error:

In member function ‘int Vec::operator[](int)’:
error: lvalue required as left operand of assignmen
In function ‘int main()’:
error: lvalue required as left operand of assignment

回答1:

You'd need to return a reference to your element - however note that even if you did, you'd run into inifinite recursion - your operator[] calls itself.

Either way - inheriting from std::vector isn't a good idea. Use composition instead.



回答2:

This particular error is caused because you are not returning an lvalue, generally defined as something that can appear to the left of an assignment, such as v[0] = 5;. You have more problems as pointed out in the other answers but this is the specific issue you face with that error message (a).

The correct specification for overloading the index operator is:

int& operator[] (const int nIndex);

You have to return a reference to the item (so it can be modified) if you want to treat it as an lvalue. The following code shows a fix, although obviously all array indexes map to the same value in this simplified case:

#include <vector>
#include <iostream>

class Vec : public std::vector<int> {
    public:
        int& operator[] (int);    // <-- note the '&'
    private:
        int xyzzy;
};

int& Vec::operator[] (int idx) {  // <-- note the '&'
    std::cout << "OK\n";
    return xyzzy;
}

int main () {
    Vec v;
    v[0] = 5;
    v[1] = 6;
    std::cout << v[22] << '\n';
    return 0;
}

The output of this is:

OK
OK
OK
6

In reality, you wouldn't map all indexes to the same value, the code above is simply to illustrate the correct function signature. I haven't bothered to give a more complete example since subclassing classes with non-virtual destructors regularly leads to problems in non-trivial code (b).


(a) It's not usually considered a good idea to subclass std::vector since the destructor isn't virtual, so you can get into trouble when trying to destroy an object polymorphically.

You're probably better off using a has-a relationship (where your class contains a vector) rather than an is-a relationship (where you inherit).

That unfortunately means you may have to create a lot of pass-through methods from your class to the underlying vector (although only the ones you need) but it will solve the problem with the destructor.


(b) See (a) :-)



回答3:

The code below illustrates how to call the operator[] from the vector base class....

#include <iostream>
#include <vector>

struct Vec : std::vector<int>
{
    int& operator[](int n)
    {
        std::cout << "operator[](" << n << ")\n";
        return std::vector<int>::operator[](n);
    }
};

int main()
{
    Vec v;
    v.push_back(10);
    v.push_back(20);
    v[0] += 5;
    std::cout << v[0] << ' ' << v[1] << '\n';
}

Output when I run it:

operator[](0)
operator[](1)
operator[](0)
15 20

Don't take all this talk about "do not inherit from std::vector" too seriously: you have to go out of your way to delete a dynamically allocated Vec using a std::vector<int>*, or do an accidental by-value slicing copy - and even then it'd probably only bite you if you had added data members. You should make sure you understand those risks then make your own assessment, but for small utility programs etc. it's productive to inherit from such classes sometimes....