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());
}
Related
Suppose I have a class that implements operator[], e.g.:
class Array {
public:
Array(size_t s) : data(new int[s]) {}
~Array() { delete[] data; }
int& operator[](size_t index) {
return data[index];
}
private:
int* data;
};
Is there a way to create a random access iterator from the class without having to manually create the iterator class and all its methods? I could manually define the class as follows:
class ArrayIterator : public std::iterator<std::random_access_iterator_tag, int> {
public:
ArrayIterator(Array& a) : arr(a), index(0) {}
reference operator[](difference_type i) {
return arr[index + i];
}
ArrayIterator& operator+=(difference_type i) {
index += i;
return *this;
}
bool operator==(const ArrayIterator& rhs) {
return &arr == &rhs.arr && index == rhs.index;
}
// More methods here...
private:
Array& arr;
difference_type index;
};
But doing so is time consuming since there are so many methods to implement, and each iterator for a class with operator[] would have the exact same logic. It seems it would be possible for the compiler to do this automatically, so is there a way to avoid implementing the entire iterator?
Is there a way to create a random access iterator from the class without having to manually create the iterator class and all its methods?
The simplest way to create a random-access iterator is to just use a raw pointer, which satisfies all of the requirements of the RandomAccessIterator concept (the STL even provides a default template specialization of std::iterator_traits for raw pointers), eg:
class Array {
public:
Array(size_t s) : data(new int[s]), dataSize(s) {}
~Array() { delete[] data; }
int& operator[](size_t index) {
return data[index];
}
size_t size() const { return dataSize; }
int* begin() { return data; }
int* end() { return data+dataSize; }
const int* cbegin() const { return data; }
const int* cend() const { return data+dataSize; }
private:
int* data;
size_t dataSize;
};
Implementing the random access operator by using operator[] may work, but it can be very inefficient and that's why compilers dont do that automatically. Just imagine adding operator[] to a class like std::list, where "going to element i" may take up to i steps. Incrementing an iterator based on operator[] would then have complexity O(n), where n is the size of the list. However, users of random access iterators, expect a certain efficiency, typically O(1).
I'm designing a class which has two standard vectors as members. I would like to be able to use range-based for loops on the vector elements and I came up with this solution
#include <iostream>
#include <vector>
using namespace std;
class MyClass {
public:
void addValue1(int val){data1_.push_back(val);}
void addValue2(int val){data2_.push_back(val);}
vector<int> const & data1() const {return data1_;}
vector<int> const & data2() const {return data2_;}
// ...
private:
vector<int> data1_;
vector<int> data2_;
// ...
};
void print1(MyClass const & mc) {
for (auto val : mc.data1()){
cout << val << endl;
}
}
void print2(MyClass const & mc) {
for (auto val : mc.data2()){
cout << val << endl;
}
}
int main(){
MyClass mc;
mc.addValue1(1);
mc.addValue1(2);
mc.addValue1(3);
print1(mc);
}
Clearly, the alternative of defining begin() and end() functions doesn't make sense since I have two distinct vectors.
I would like to ask the following questions:
A shortcoming of the proposed solution is that the contents of the two vectors cannot be changed (due to the const qualifier). In the case I need to modify the vector elements how can I modify the code?
EDIT: the modification should preserve encapsulation
Considering data encapsulation, do you think it is bad practice to return a (const) reference to the two vectors?
Use something like gsl::span<int> and gsl::span<const int>.
Here is a minimal one:
template<class T>
struct span {
T* b = 0; T* e = 0;
T* begin() const { return b; }
T* end() const { return e; }
span( T* s, T* f ):b(s),e(f) {}
span( T* s, std::size_t len ):span(s, s+len) {}
template<std::size_t N>
span( T(&arr)[N] ):span(arr, N) {}
// todo: ctor from containers with .data() and .size()
// useful helpers:
std::size_t size() const { return end()-begin(); }
bool empty() const { return size()==0; }
T& operator[](std::size_t i) const { return begin()[i]; }
T& front() const { return *begin(); }
T& back() const { return *(std::prev(end())); }
// I like explicit defaults of these:
span() = default;
span(span const&) = default;
span& operator=(span const&) = default;
~span() = default;
};
now you can write:
span<int const> data1() const {return {data1_.data(), data1_.size()};}
span<int const> data2() const {data2_.data(), data2_.size()};}
span<int> data1() {return {data1_.data(), data1_.size()};}
span<int> data2() {data2_.data(), data2_.size()};}
A shortcoming of the proposed solution is that the contents of the two vectors cannot be changed (due to the const qualifier). In the case I need to modify the vector elements how can I modify the code?
First of all, you should add a data1() and a data2() not-const versions that return a reference to the data1_ and data2_ members
vector<int> const & data1() const {return data1_;}
vector<int> const & data2() const {return data2_;}
vector<int> & data1() {return data1_;}
vector<int> & data2() {return data2_;}
Second: if you want modify the element in print1() (by example) you have to receive mc as not const reference
// ..........vvvvvvvvv no more const
void print1 (MyClass & mc) {
so you can change mc.
Third: in the range based loop you have to define val as reference so you can modify it modifying also the referenced value inside the vector
// ........V by reference
for ( auto & val : mc.data1() ) {
++val ; // this modify the value in the vector inside mc
cout << val << endl;
}
Considering data encapsulation, do you think it is bad practice to return a (const) reference to the two vectors?
IMHO: if the reference is const, not at all: it's a good practice because permit the safe use of the member without the need to duplicate it.
If the reference isn't const, I don't see big difference with declaring the member public.
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!!!
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.)
Let's say I have a container class called MyContainerClass that holds integers.
The [] operator, as you know, can be overloaded so the user can more intuitively access values as if the container were a regular array. For example:
MyContainerClass MyInstance;
// ...
int ValueAtIndex = MyInstance[3]; // Gets the value at the index of 3.
The obvious return type for operator[] would be int, but then the user wouldn't be able to do something like this:
MyContainerClass MyInstance;
MyInstance[3] = 5;
So, what should the return type for operator[] be?
The obvious return type is int& :)
For increased elaboration:
int &operator[](ptrdiff_t i) { return myarray[i]; }
int const& operator[](ptrdiff_t i) const { return myarray[i]; }
// ^ could be "int" too. Doesn't matter for a simple type as "int".
This should be a reference:
int &
class MyContainerClass {
public:
int& operator[](unsigned int index);
int operator[](unsigned int index) const;
// ...
};
Returning a reference lets the user use the result as an lvalue, as in your example MyInstance[3] = 5;. Adding a const overload makes sure they can't do that if MyInstance is a const variable or reference.
But sometimes you want things to look like that but don't really have an int you can take a reference to. Or maybe you want to allow multiple types on the right-hand side of MyInstance[3] = expr;. In this case, you can use a dummy object which overloads assignment:
class MyContainerClass {
private:
class Index {
public:
Index& operator=(int val);
Index& operator=(const string& val);
private:
Index(MyContainerClass& cont, unsigned int ind);
MyContainerClass& m_cont;
unsigned int m_ind;
friend class MyContainerClass;
};
public:
Index operator[](unsigned int ind) { return Index(*this, ind); }
int operator[](unsigned int ind) const;
// ...
};
int&
returning a reference allows you too use the returned value as a left-hand side of the assignment.
same reason why operator<<() returns an ostream&, which allows you to write cout << a << b;