Is opIndexAssign possible in C++? - c++

The D Programming Language Version 2 has a nifty method to overload an expression like this:
classInstance[someName] = someValue;
Or as D Function defined in this little example:
ref Map opIndexAssign(ref const(ValueT) value, ref const(NameT) name)
{
this.insert(name, value);
return this;
}
Is this possible in C++ (ideally without using the STL)? If so, how?

Normally, you would use a proxy object as the return type of operator[]; that object will have a custom operator= defined. The vector<bool> specialization in the C++ Standard Library uses a proxy to get the behavior you are looking for. The proxy-based solution isn't as transparent as the D version, though. The code is something like:
class proxy;
class my_map {
public:
proxy operator[](const key_type& k);
// Rest of class
};
class proxy {
my_map& m;
key_type k;
friend class my_map;
proxy(my_map& m, const key_type& k): m(m), k(k) {}
public:
operator value_type() const {return m.read(k);}
proxy& operator=(const value_type& v) {m.write(k, v); return *this;}
};
proxy my_map::operator[](const key_type& k) {
return proxy(*this, k);
}

Not being familiar with D, I'm going to have to assume that your first code snippet ends up calling opIndexAssign with value = someValue and name = someName?
If so, it can be done in C++, but not in such a simple manner. You could overload the [] operator, and return a proxy object with a custom = operator as follows (very basic, contrived example):
class MyProxy
{
public:
MyProxy (int& ref) : valueRef(ref) { }
MyProxy& operator = (int value) { valueRef = value; return *this; }
private:
int& valueRef;
};
class MyClass
{
public:
MyProxy operator [] (std::string name);
private:
int myVal;
};
MyProxy& MyClass::operator [] (std::string name)
{
if (name.compare("myVal"))
return MyProxy(myVal);
...
}
int main ( )
{
MyClass mc;
mc["myVal"] = 10; // Sets mc.myVal to 10
}
I'd like to strongly stress that the above is not very pretty/well formed code, just an illustration. It has not been tested.
EDIT: Too quick for me Jeremiah!!!

Related

Overloading operator[] only for members of a specific class

Edit: MyClass has been renamed to ReverseStringAccess for disambiguation.
I have a class which encapsulates a vector<string>. The class has an overloaded operator[] which can be used to read and modify the contents of the vector. This is how it looks (minimally):
class ReverseStringAccess {
public:
ReverseStringAccess() {}
ReverseStringAccess(vector<string> _arr) arr(_arr) {}
string& operator[](int index) {
return arr[index];
}
private:
vector<string> arr;
};
Now I need to be able to modify the contents of each string in the vector without directly accessing the vector (i.e. some sort of operator[][] which only works with vectors that are members of this class). The problem is that using ReverseStringAccess[][] will result in the default behavior of operator[] on strings. For example, this statement:
ReverseStringAccess[i][j]
would give the jth character of the ith string in the vector. But I want it to (for example) instead get the (length - j - 1)th character of the ith string in the vector, where length is the length of the ith string.
Is this possible? If yes, how?
If you want my_object[i][k] to not invoke std::string::operator[] then don't return a std::string& from MyClass::operator[], but a proxy that implements the desired []:
class MyClass {
public:
MyClass() {}
MyClass(vector<string> _arr) arr(_arr) {}
struct MyProxy {
std::string& str;
char& operator[](size_t index) { /.../ }
const char& operator[](size_t index) const { /.../ }
};
MyProxy operator[](int index) {
return {arr[index]};
}
private:
vector<string> arr;
};
The question is how to achieve reverse indexing on members of vector<string> via [][].
This can be achieved via a wrapper class for std::string:
template<typename T>
class reverse_access {
T& ref;
public:
// create wrapper given reference to T
reverse_access(T& ref_)
: ref(ref_)
{}
// reverse access operator []
auto operator[](size_t i) -> decltype(ref[0])
{ return ref[ ref.size() - 1 - i]; }
auto operator[](size_t i) const -> decltype(ref[0])
{ return ref[ ref.size() - 1 - i]; }
// allow implicit conversion to std::string reference if needed
operator T&() { return ref; }
operator const T&() const { return ref; }
};
// inside MyClass:
reverse_access<string> operator[](int index) {
return reverse_access<string>(arr[index]);
}
reverse_access<const string> operator[](int index) const {
return reverse_access<const string>(arr[index]);
}
And use return reverse_access<std::string>(arr[index]); in operator[] in MyClass.
The added conversion operator allows implicit conversion to a reference to the original type.
Another option is to use a different operator that can take 2 arguments: myclass(vec_index,str_index):
string::reference operator()(size_t vec_index, size_t str_index)
{
return arr[vec_index][ arr[vec_index].size() - str_index ];
}
string::const_reference operator()(size_t vec_index, size_t str_index) const
{
return arr[vec_index][ arr[vec_index].size() - str_index ];
}
A third solution is to reverse all strings on construction of ReverseStringAccess:
ReverseStringAccess(vector<string> _arr) arr(_arr)
{
for (auto& str : arr)
std::reverse(str.begin(), str.end());
}

C++ operator overloading [], where is the data parameter to assign?

I want to add a overload the operator [] in my class. Operator overloading is not something I've had to do before.
I want to write an implementation to do the following:
myclass a;
a["test"] = 123;
int test = a["test"];
So far in my class the prototype looks like this:
string operator[](const char* cpszLabel);
The implementation isn't complete and looks like this:
string myclass::operator[](const char* cpszLabel) {
string strContent;
if ( cpszLabel != nullptr ) {
}
return strContent;
}
What I'm not sure about is how to reference the data that is being assigned or does this require overloading the '=' too?
I've added an overload for the '=' operator, but this doesn't get called:
Prototype:
string operator=(int intData);
Implementation:
string myclass::operator=(int intData) {
char szString[24];
sprintf(szString, "\"%d\"", intData);
return string(szString);
}
You need to arrange things so operator[](const char* cpszLabel) returns a reference to something in your class.
int& operator[](const char* cpszLabel);
is probably a better prototype.
You can then modify that "something" in your class via that reference. To be honest though what you want can be achieved with
typedef std::map<std::string, int> myclass;
and most folk don't bother with the typedef, especially now that we have auto. If you want to use a std::map as a member variable in the class (in order to reduce functionality &c.), then the following is a starting point:
class myclass
{
std::map<std::string, int> m_data;
public:
int& operator[](const char* cpszLabel)
{
return m_data[cpszLabel];
}
};
In a["test"] = 123;, the "receiver" of the assignment is the object that's returned from the lookup, which is a string.
You can't overload string's assignment operator.
But, as is well known, every problem can be solved by introducing a level of indirection.
You can store a type of your own instead of std::string, and let that handle the conversion.
A very small example as illustration:
struct Data
{
template<typename T>
Data& operator=(const T& rhs)
{
std::ostringstream os;
os << rhs;
value = os.str();
return *this;
}
operator const char*() const { return value.c_str(); }
std::string value;
};
struct Container
{
Data& operator[] (const std::string& s) { return table[s]; }
std::map<std::string, Data> table;
};
int main()
{
Container cont;
cont["foo"] = "bar";
cont["baz"] = 123;
cont["goo"] = 5.5;
for (auto v: cont.table)
{
std::cout << v.first << " --> " << v.second << '\n';
}
}
Output:
baz --> 123
foo --> bar
goo --> 5.5

Sorting list of shared pointers

Given the class
class objects {
public:
bool compareArea (const objects& obj) const { return this->area < obj.area; }
private:
double area;
};
I want to sort a
list<shared_ptr<objects>> myObjects;
I cannot use a lambda (since my toolchain's C++11 support is incomplete). Thus, I tried the following:
using namespace placeholders;
myObjects.sort(bind(&objects::compareArea,_1,_2));
This line is called from another file (not from a class member!). The problem is, that compareArea requires two objects as input. But I give two shared pointer to objects to it. Is there an easy way of how to include the dereferencing of the pointers into the sort-call? I want the objects::compareArea(..) function to stay as it is. I do not want this kind of solution
bool compareArea (const shared_ptr<objects>& ptr1, const shared_ptr<objects>& ptr2) {
return ptr1->area > ptr2->area;
}
// in same source-file:
myObjects.sort(bind(compareArea,_1,_2));
where compareArea is no member-function of objects. Actually an operator overloading of < would be my favourite solution.
I would strongly suggest that you never store any kind of pointer in a container.
Instead, make a handle class which supports the required arithmetic and comparison operators.
It makes for code that's easier to reason about:
class objects {
public:
objects(double w, double h) : area(w * h) {}
bool operator<(const objects& r) const { return this->area < r.area; }
private:
double area;
};
struct object_handle
{
object_handle(shared_ptr<objects> const& ptr) : ptr_(ptr) {}
static object_handle create(double w, double h) { return make_shared<objects>(w,h); }
bool operator < (object_handle const& r) const {
return *ptr_ < *r.ptr_;
}
shared_ptr<objects> ptr_;
};
int main() {
std::vector<object_handle> mylist;
mylist.push_back(object_handle::create(10, 7));
mylist.push_back(object_handle::create(2, 5));
std::sort(mylist.begin(), mylist.end());
}
Lambdas are just syntactic sugar for a class with operator(), so you can very easily write one directly (especially if you don't need captures):
struct Comparator
{
bool operator() (const shared_ptr<objects> &lhs, const shared_ptr<objects> &rhs) const
{
return lhs->compareArea(*rhs);
}
};
myObjects.sort(Comparator());

Determining Purpose of operator[] Usage

Let's say I have something like the following method in my container class:
Datatype& operator[](const unsigned int Index) // I know this should use size_t instead.
{
return *(BasePointer + Index); // Where BasePointer is the start of the array.
}
I'd like to implement some sort of bounds-checking for the MyInstance[Index] = Value usage so the container resizes automatically if the user tries to change a value outside its range. However, I want something else to happen if the user tries to access a value outside the container's range, e.g. MyVariable = MyInstance[Index]. How can I detect how operator[] is being used?
Sketch:
return a proxy object instead of the actual data entry. The proxy object then defines operator = to handle the assignment case, and an implicit conversion operator for the reading-out case.
template <typename T>
class AccessorProxy {
friend class Container<T>;
public:
AccessorProxy(Container<T>& data, unsigned index)
: data(data), index(index) { }
void operator =(T const& new_value) {
// Expand array.
}
operator const T&() const {
// Do bounds check.
return *(data.inner_array + index);
}
private:
AccessorProxy(const AccessorProxy& rhs)
: data(rhs.data), index(rhs.index) {}
AccessorProxy& operator=(const AccessorProxy&);
Container<T>& data;
unsigned index;
};
template <typename T>
class ConstAccessorProxy {
friend class Container<T>;
public:
ConstAccessorProxy(const Container<T>& data, unsigned index)
: data(data), index(index) { }
operator const T&() const {
// Do bounds check.
return *(data.inner_array + index);
}
private:
ConstAccessorProxy(const ConstAccessorProxy& rhs)
: data(rhs.data), index(rhs.index) {}
ConstAccessorProxy& operator=(const ConstAccessorProxy&);
const Container<T>& data;
unsigned index;
};
AccessorProxy<Datatype> operator[](const unsigned int Index)
{
return AccessorProxy<Datatype>(*this, Index);
}
ConstAccessorProxy<Datatype> operator[] const (const unsigned int Index)
{
return ConstAccessorProxy<Datatype>(*this, Index);
}
The accessor classes will likely need to be be friends of the container class.
Finding ways to avoid the code duplication is left as an exercise to the reader. :)
Use a dummy class type to represent expressions like MyInstance[Index] and delay figuring out what to do until that expression is used.
class MyContainer {
private:
class IndexExpr {
public:
// Get data from container:
operator const Datatype&() const;
// Expand container if necessary, then store data:
Datatype& operator=(const Datatype& value);
// Treat MyInstance[i] = MyInstance[j]; as expected:
Datatype& operator=(const IndexExpr& rhs)
{ return *this = static_cast<const Datatype&>(rhs); }
private:
IndexExpr(MyContainer& cont, unsigned int ind);
MyContainer& container_;
unsigned int index_;
friend class MyContainer;
};
public:
IndexExpr operator[](unsigned int Index)
{ return IndexExpr(*this, Index); }
// No IndexExpr needed when container is const:
const Datatype& operator[](unsigned int Index) const;
// ...
};
This is not a perfect answer to "how to detect", but, if the user is accessing the operator[] via a const instance, then throw an exception if the index is out of bounds.
i.e.
Datatype const& operator[]() const { .. // don't modify here, throw exception
However, if the user is accessing the instance via a non const instance, then by all means expand if the index is out of bounds (and within your acceptable ranges)
Datatype& operator[]() { .. // modify here
Basically, you are using the const attribute of the instance to determine what your semantics would be (as done in std::map - i.e. trying to call operator[] on a const instance of a map results in a compiler error - i.e. there is no const qualified operator[] for map, because the function is guaranteed to create a mapping if the key does not exist already.)

Does C++ have an equivilent to Python's __setitem__

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";
basically, you overload the subscript operator (operator[]), and it returns a reference (so it can be read as well as written to)
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.
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";
}
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.