2

Just as the title asks, does C++ have the equivalent of Python's setitem and getitem for classes?

Basically it allows you to do something like the following.

MyClass anObject;

anObject[0] = 1;
anObject[1] = "foo";
Scott
  • 5,135
  • 12
  • 57
  • 74
  • It may help if you explain what "setitem/getitem for classes" is about. – sellibitze Oct 04 '09 at 09:26
  • There's an interesting discussion about what's possible (and what's not) in C++ with subscript operators in [this article](http://www.ddj.com/cpp/184406037). – DannyT Oct 04 '09 at 10:10

4 Answers4

7

basically, you overload the subscript operator (operator[]), and it returns a reference (so it can be read as well as written to)

newacct
  • 119,665
  • 29
  • 163
  • 224
6

You can overload the [] operator, but it's not quite the same as a separate getitem/setitem method pair, in that you don't get to specify different handling for getting and setting.

But you can get close by returning a temporary object that overrides the assignment operator.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • I think I get what you're saying. To override the behavior of a statement like "anObject[0] = 1" I would need to override both operators, but the return type of the [] operator would need to be a separate instance of a class that overrides the = operator? – Scott Oct 04 '09 at 08:44
  • 1
    Yes. Then the real problems start with the getting. The returned temporary object will not be the same type as the "logical" type (e.g. `int` or `string` in your example), so it has to have a `value()` function to get the actual value. You can improve on this slightly by overriding the conversion operator, but that has its own drawbacks, and doesn't cover every situation, so you definitely need the `value()` function as a minimum. – Daniel Earwicker Oct 04 '09 at 09:31
  • This technique is discussed in More Effective C++, Item 30, where Meyers refers to them as "proxy classes." Here's an example from a previous question. http://stackoverflow.com/questions/994488/what-is-proxy-class-in-c/994925#994925 – AFoglia Oct 05 '09 at 15:19
  • they are easy to use wrongly though - e.g. swap(a[i], a[i+1]) won't work as expected when a[i] returns a proxy class. – peterchen Oct 05 '09 at 16:01
1

To expand on Earwicker post:

#include <string>
#include <iostream>

template <typename Type>
class Vector
{
public:
    template <typename Element>
    class ReferenceWrapper
    {
    public:
        explicit ReferenceWrapper(Element& elem)
         : elem_(elem)
        {
        }

        // Similar to Python's __getitem__.
        operator const Type&() const
        {
            return elem_;
        }

        // Similar to Python's __setitem__.
        ReferenceWrapper& operator=(const Type& rhs)
        {
            elem_ = rhs;
            return *this;
        }

        // Helper when Type is defined in another namespace.
        friend std::ostream& operator<<(std::ostream& os, const ReferenceWrapper& rhs)
        {
            return os << rhs.operator const Type&();
        }

    private:
        Element& elem_;
    };

    explicit Vector(size_t sz)
     : vec_(sz)
    {
    }

    ReferenceWrapper<const Type> operator[](size_t ix) const
    {
        return ReferenceWrapper<const Type>(vec_[ix]);
    }

    ReferenceWrapper<Type> operator[](size_t ix)
    {
        return ReferenceWrapper<Type>(vec_[ix]);
    }

private:
    std::vector<Type> vec_;
};

int main()
{
    Vector<std::string> v(10);
    std::cout << v[5] << "\n";

    v[5] = "42";
    std::cout << v[5] << "\n";
}
dalle
  • 18,057
  • 5
  • 57
  • 81
  • Now try `v[5].size()` to get the size of the string you stored. Doesn't work. You can use a butt-ugly cast, or `std::string &got = v[5];` followed by `got.size()`, or add a `value()` function to `ReferenceWrapper`. – Daniel Earwicker Oct 04 '09 at 13:48
1

It's not portable, but MSVC has __declspec(property), which also allows indexers:

struct Foo
{
   void SetFoo(int index, int value) { ... }
   int GetFoo(int index) { ... }

   __declspec(property(propget=GetFoo, propput=SetFoo)) int Foo[]; 
}

other than that, Earwicker did outline the portable solution, but he's right that you'll run into many problems.

peterchen
  • 40,917
  • 20
  • 104
  • 186