I have a C++ class which simulates an array and for manipulating its members I implemented two functions: set(size_t index, size_t value) and get(size_t index). I would like to overload the [] operator to have the following functionality:
MyCustomArray[index] = value //->set(size_t index, size_t value)
And
value = MyCustomArray[index] //->get(size_t index)
get can be easily implemented with overload, but I don't know how to implement set because I need the parameter value beforehand.
My class is an implementation of a fixed-word array (elements in the array have at most P bits, where P is a parameter and it can be less than the regular machine word). To support this functionality, set and get manipulate a range of bits of a value in a regular C/C++ array.
Is it possible to overload in this scenario?
Thanks in advance!
This is just like what std::vector::operator[] is doing - using a proxy object.
class MyCustomArray
{
public:
using value_type = unsigned;
class Proxy
{
public:
friend class MyCustomArray;
operator value_type() const
{
return m_customArray.get(m_index);
}
Proxy & operator=(value_type value)
{
m_customArray.set(m_index, value);
return *this;
}
private:
Proxy(MyCustomArray & customArray, size_t index)
: m_customArray(customArray), m_index(index) {}
MyCustomArray & m_customArray;
size_t m_index;
};
value_type operator[](size_t index) const
{
return get(index);
}
Proxy operator[](size_t index)
{
return Proxy(*this, index);
}
value_type get(size_t index) const;
void set(size_t index, value_type value);
private:
/// The data goes here
};
Then
void work(MyCustomArray & arr)
{
// Return a Proxy object, and call operator= over it.
arr[3] = 5;
// arr_2 is of type MyCustomArray::Proxy
auto arr_2 = arr[2];
arr_2 = 1; // modifies arr[2]
unsigned x = arr_2; // gets 1 from arr[2]
// This works, due to auto conversion to value_type:
std::cout << arr_2 << '\n';
}
As mentioned in the comments, this can be accomplished by having operator[] return a proxy object, which is how std::vector<bool> performs its magic.
In your case, it would look something along these lines:
struct MyCustomArray;
struct ArrayMemberRef {
MyCustomArray* target_;
std::size_t index_;
ArrayMemberRef& operator=(std::size_t value);
operator std::size_t();
};
struct MyCustomArray {
ArrayMemberRef operator[](std::size_t index) {
return ArrayMemberRef{this, index};
}
void set(std::size_t index, std::size_t value);
int get(std::size_t index);
};
ArrayMemberRef& ArrayMemberRef::operator=(std::size_t value) {
target_->set(index_, value);
return *this;
}
ArrayMemberRef::operator std::size_t() {
return target_->get(index_);
}
Related
Is there a way I could avoid using Range2 as a name and have both classes named Range? I'm a bit confused with the C++ template syntax.
template <int BEGIN, int END>
class Range2
{
public:
class Iterator
{
private:
int n;
public:
Iterator(int n)
: n(n)
{
}
int operator *() const
{
return n;
}
Iterator const & operator ++()
{
++n;
return *this;
}
bool operator !=(Iterator const & i) const
{
return n != i.n;
}
};
Iterator begin() const
{
return Iterator(BEGIN);
}
Iterator end() const
{
return Iterator(END);
}
};
template<int END>
class Range
: public Range2<0, END>
{
};
As with function arguments, in C++ template arguments can have a default value. The only thing you will have to pay for this putting the END, which has no default value, before the BEGIN, which has 0 as default.
// Here we add the default parameter to BEGIN
// The arguments are switched because END is without a default
// parameter, so it has to come before BEGIN that has one
template <int END, int BEGIN=0>
// The class itself is the same, but now you can use it
// without giving a BEGIN parameters
class Range
{
public:
class Iterator
{
private:
int n;
public:
Iterator(int n)
: n(n)
{
}
int operator *() const
{
return n;
}
Iterator const & operator ++()
{
++n;
return *this;
}
bool operator !=(Iterator const & i) const
{
return n != i.n;
}
};
Iterator begin() const
{
return Iterator(BEGIN);
}
Iterator end() const
{
return Iterator(END);
}
};
It compiles and should work as intended. Without a main, I wasn't able to test it, though.
EDIT: I added some comments and here an example of usage, just for clarity:
Range<10, 3> r(3); /*here we use it as usual, pay attention begin is
now the second argument and not the first */
Range<10> r(0); /* here we don't give a BEGIN argument, the
compiler will use the default one */
hello i want to create simple array class to get value and insert value like (array[0] = 4) and this is my program but i have problem to use [] = in same time for insert
template <typename Param>
class arr
{
private:
int Last_point = 0;
Param Data[];
public:
void& operator[]=(int Element_id, Param v)
{
Data[Element_id] = v;
}
Param& operator[] (int Element_id)
{
return Data[Element_id];
}
};
void main()
{
arr <int> Array;
Array[1] = 555;
cout << "Is(" << to_string(Array[1]) << ")" << endl;
system("pause");
}
is there any operator like ([]=)? or for this, i have to use which methods? also i want to get value if just used []
The syntax you are attempting for the operator[] functions is quite wrong. You need something like:
// Version for non-const objects
Param& operator[](std::size_t i)
{
return Data[i];
}
// Version for const objects
Param const& operator[](std::size_t i) const
{
return Data[i];
}
If you want to support arrays whose sizes are known at compile time, you can use:
template <typename Param, std::size_t N>
class arr
{
private:
Param Data[N];
public:
Param& operator[](std::size_t i)
{
return Data[i];
}
Param const& operator[](std::size_t i) const
{
return Data[i];
}
};
If you want to support arrays whose sizes are known at run time, you can use the following. However, you need to be aware of The Rule of Three and make sure to implement the copy constructor and the copy assignment operator properly.
template <typename Param>
class arr
{
private:
Param* Data;
public:
arr(size_t size) : Data(new Param[size]) {}
~arr() { delete [] Data; }
Param& operator[](std::size_t i)
{
return Data[i];
}
Param const& operator[](std::size_t i) const
{
return Data[i];
}
};
Foo& operator[] is sufficient for reading and writing.
Include the size of the array as a template parameter, or replace Param[] with std::vector<Param>.
Use size_t instead of int for array index types.
You don't need to wrap Array[1] in to_string for output.
Don't use void main! The standard says main must be int.
void& is not valid C++ either.
#include <iostream>
using namespace std;
template <typename Param, size_t Size> class arr {
private:
Param Data[Size];
public:
Param &operator[](size_t Element_id) {
return Data[Element_id];
}
};
int main() {
arr<int, 3> Array;
Array[1] = 555;
cout << "Is(" << Array[1] << ")" << endl;
}
However, all that arr does in my snippet is be a less useful std::array!
UPDATE: I revised some place, and now the problem has changed in some way.
I'm writing a C++ class. Like:
class qqq{
map<int,int> core;
//......
int& operator[](int n){return core[n];};
};
int main(){
qqq a;
a[3]=7;a[5]=0;//Case a
int b=a[3];//Case b
return 0;
}
Although case A and case B are calling the same function(overloaded operator), but case a is used as an lvalue while case b is used as a rvalue.
For some reason, I want to have the effect that if 0 is passed to a[5], delete the node 5 in core. Like:
int& operator[](int n){
if(CASE A && THE VALUE PASSED TO IT IS 0)
core.erase(core.find(n));
else
return core[n];
}
Maybe my description is not accurate.
Here is an implementation of the proxy pattern mentioned in the comments.
Personally, I don't use this, my maps are wrapped in classes that don't provide operator[] at all, instead there are functions like .get(key, default) .init(key), .setdefault(key, default), etc. depending on the class.
// This code is C++11 but it's not essential to the problem.
// The current code calls copy constructors more than necessary.
#include <map>
#include <cassert>
template<class K, class V>
struct zero_map
{
struct proxy
{
std::map<K, V> *container;
K key;
operator V()
{
auto it = container->find(key);
if (it == container->end())
return V();
return *it;
}
void operator = (V value)
{
if (value == V())
{
container->erase(key);
}
else
{
// probably should use .insert() and conditionally assign
(*container)[key] = value;
}
}
};
std::map<K, V> _inner;
proxy operator[](K k)
{
return proxy{&_inner, k};
}
};
int main()
{
zero_map<int, int> foo;
assert (foo._inner.size() == 0);
foo[1] = 0;
assert (foo._inner.size() == 0);
foo[0] = 1;
assert (foo._inner.size() == 1);
foo[0] = 0;
assert (foo._inner.size() == 0);
}
As a comment said, use a proxy class.
template<typename T, size_t BadIndex>
class Element{ // please use a more meaningful name
public:
Element(const size_t index): index(index){}
operator T& (){return value;}
operator T const&() const{return value;}
T &operator =(const T &rhs){
if(index != BadIndex)
value = rhs;
return value;
}
operator T const&() const{return value;}
operator T&(){return value;}
private:
T value;
const size_t index;
};
class qqq{
public:
std::map<int, Element<int, 5>> core;
Element<int> &operator [](size_t index){
auto itt = core.find(index);
if(itt == core.end()){
core.emplace(index, index);
itt = core.find(index);
}
return (*itt).second;
}
};
That should work, but 5 will always give you a garbage result.
You have to always return a value which can be used as left value in the assignment expression. Therefore, I suggest to use a garbage int variable. I declared the garbage as static because we need just one instance of this variable and we don't care its value.
For example,
class qqq{
static int garbage;
map<int,int> core;
//......
int& operator[](int n){
if(CASE A && THE VALUE PASSED TO IT IS 0)
return garbage;
else
return core[n];
}
};
However, this solution is confusing in my point of view because the behaviour completely changes according to what you specify in the square brackets. If the value passed in input is incorrect, I would probably thrown an exception.
* EDIT *
I think you are over complicating the problem using the [] operator. You can easily solve your problem by using setter and getters. For example :
int set(int index, int value){
if( value == 0)
core.erase(core.find(index));
else
return core[index];
}
int get(int index) {
return core[index];
}
The [] allows only for returning a reference, you don't know what is the value used in the assignment.
You question is now clear, unfortunately you will have no way to do that is C++. operator[] is not a getter and a setter : it can only return a reference, and that reference is than used for a mere assignement. At the moment the operator returns its reference, you cannot know what value will be used for a assignement, and you can hardly know how the ref will be used.
IMHO what you need is more :
int getCore(int i) {
return core[i];
}
void setCore(int i, int newval) {
if (newval == 0) {
core.erase(core.find(i));
}
else {
core[i] == newval;
}
I have a C datastructure representing a vector of boolean values; for reasons outside of my control the bools' are stored internally as integers with two magical values (not 0 and 1 ...) representing true and false. I have created a C++ class wrapping this C structure, and it works nicely. I have implemented the set()and get()methods as:
void Class::set(size_t index , bool value) {
if (value)
c_ptr[index] = SPECIAL_TRUE_VALUE;
else
c_ptr[index] = SPECIAL_FALSE_VALUE;
}
This works ok; but ideally I would like to overload operator[], however it is not clear to me how/if I can do that - due to special transformation between bool and the integer values?
struct pseudo_reference {
operator bool()const&&{
return c->get(index);
}
pseudo_reference operator=(bool b)&&{
c->set(index, b);
return {c,index};
}
// sometimes having named functions is useful:
bool get() const&& {
return std::move(*this);
}
void set(bool b)&& {
std::move(*this) = b;
}
pseudo_reference()=delete;
private:
Class* c;
size_t index;
pseudo_reference(pseudo_reference&&o)=default; // not exposed
pseudo_reference(Class* pc, size_t i):c(pc),index(i){}
friend class Class;
};
In Class:
pseudo_reference operator[](size_t i){
return {this, i};
}
bool operator[](size_t i)const{
return c_ptr[index] == SPECIAL_TRUE_VALUE;
}
I stored both a pointer and an index, so I avoid reimplementing the logic of get/set in my pseudo_reference. Such pseudo_references are likely to be short-lived, so size optimization probably isn't important.
I blocked all non-rvalue operations to discourage storing a pseudo_reference. You can make said operations non-rvalue restricted relatively harmlessly, but in my experience pseudo_references are values that behave like references, so it is better if they don't persist.
Someone can still store a pseudo_reference via auto&& x = c[33];, but using it without moveing it won't be possible. Hopefully that catches most error-prone uses of it. auto x = c[33]; won't work.
To implement operator[](), you need to return a proxy object that does the actual assignment when it appears on the left-hand-side of =:
struct proxy {
proxy& operator=( bool value ) {
c_.c_ptr[ index_ ] = value ? SPECIAL_TRUE_VALUE : SPECIAL_FALSE_VALUE;
return *this;
}
operator bool() const { // for when it's just used normally, not =
return c_ptr[ index ] == SPECIAL_TRUE_VALUE;
}
private:
Class &c_;
size_t const index_;
proxy( Class &c, size_t index ) : c_( c ), index_( index ) { }
friend class Class;
}
class Class {
public:
proxy operator[]( size_t index ) {
return proxy( *this, index );
}
bool operator[]( size_t index ) const { // read-only access is easy
return c_ptr[ index ] == SPECIAL_TRUE_VALUE;
}
// ...
};
Or something like that.
You can return a wrapper helper class which handles assignment for you.
struct WrapMe {
c_ptr_T &value;
WrapMe(c_ptr_T &_value) : value(_value) {}
// handles assignment of bool values
WrapMe & operator=(const bool b) {
value = (b) ? SPECIAL_TRUE_VALUE : SPECIAL_FALSE_VALUE;
return *this;
}
// handles cast to bool
operator bool() const { return value == SPECIAL_TRUE_VALUE; }
};
class Class {
WrapMe operator[](const int idx) { return WrapMe(c_ptr[idx]); }
// ...
};
I have written a templates class for storing multiple bools in an integer.
Right now, setting and getting each bool is done with explicit functions
bool isBitSet(int index)
{
return static_cast<bool>((block_ >> index) % 2)
}
void setBitOn(int index)
{
block_ |= 1 << index;
}
I believe that the following would work for getting a value, but how would setting work since we can't directly return a reference for a bit?
const bool operator [] (int index) const
{
return static_cast<bool>((block_ >> index) % 2);
}
The same is done in std::vector<bool> and in std::bitset in the standard library. As stated in the reference, std::vector<bool> it returns a proxy class that has its operators overloaded to act as an element of the vector.
You could to that as well.
For a user-friendly example see again the reference for a public interface, it is something like this:
template <class Allocator>
class vector<bool, Allocator> {
// ...
public:
class reference {
friend class vector;
reference();
public:
~reference();
operator bool() const;
reference& operator=(bool x);
reference& operator=(const reference&);
void flip();
};
// ...
};
To implement this class you should store a member pointer to your actual data block and a mask to operate with.
For a real example, in the g++ headers look for member class of std::vector<bool> called std::vector<bool>::_Bit_reference in the file bits/stl_bvector.h.
To clarify the OP with an example:
Let's say you have a class containing 320 bools. You could write it as:
class boolcontainer {
uint32_t data[10];
public:
//default ctor. to initialize the elements with zeros
boolcontainer() { for (int i = 0; i < 10; ++i) { data[i] = 0; } }
}
You want to add an operator[]. To add a const one is easy:
class boolcontainer {
uint32_t data[10];
public:
bool operator[](int i) const { return data[i/32] & (1 << (i%32)); }
}
to have a non-const one you need much more. First you need to create a class that represents a reference to your value. You must have some kind of pointer to where the value is stored and (in this case) you need a bitmask to specify one concrete bit. To be able to handle this as a bool& you need to add some operators, namely conversion to bool and operator=:
class reference {
uint32_t *dataptr;
uint32_t mask;
public:
//constructor just initializing members
reference(uint32_t *dataptr_, uint32_t mask_) : dataptr(dataptr_), mask(mask_) {}
//conversion to bool
operator bool() const {
//just like in the getter, but the bitmask is stored now locally
return *dataptr & mask;
}
//sets one single bit represented by mask to b
reference& operator=(bool b) {
if (b) {
*dataptr |= mask;
} else {
*dataptr &= ~mask;
}
return *this;
}
//TODO copy ctor., operator==, operator<
};
Note that the above struct will behave as a bool& -- reading from it reads the value from the data point represented by the pointer and the mask, and similarly, writing to it overwrites the bit at the represented location. I also wrote a constructor that initializes the members.
Now all you need is that your boolcontainer's operator[] should return an object of the above class:
class boolcontainer {
uint32_t data[10];
public:
boolcontainer() { for (int i = 0; i < 10; ++i) { data[i] = 0; } }
class reference {
... //see above
}
//keep the const version for efficiency
bool operator[](int i) const { return data[i/32] & (1 << (i%32)); }
//non-const version returns our reference object.
reference operator[](int i) { return reference(&data[i/32], 1 << (i%32)); }
};
And now some code to test it (prints only the first 40 values):
#include <iostream>
#include "boolcontainer.h"
void printboolcontainer(const boolcontainer &bc)
{
//note that this is the constant version
for (int i = 0; i < 40; ++i) {
std::cout << bc[i];
}
std::cout << std::endl;
}
int main()
{
boolcontainer bc;
printboolcontainer(bc);
bc[0] = true;
bc[3] = true;
bc[39] = true;
printboolcontainer(bc);
}