This question already has answers here:
C++: Overloading the [ ] operator for read and write access
(3 answers)
Closed 7 years ago.
I'm currently attempting to overload the '[]' operator for both read and write operations. I have created them like the following:
V operator[] (K key) const; //Read
V& operator[] (K key); //Write
However, only the 'write' is called from both the following:
foo["test"] = "bar"; //Correct, will use 'write'
cout << foo["test"]; //Incorrect, will use 'write'
What is the reason for this and is there a possible solution?
Same question that didn't help, found here: C++: Overloading the [ ] operator for read and write access
Although, the solution presented did not work as intended, and still only the write overload was accessed.
Overloading is done based on the static type of the argument. If the object foo you use an operator with is non-const the non-const overload is used. If it is const the const overload is used.
If you want to distinguish between reading and writing you'll need to return a proxy from your subscript operator which converts to the suitable type for reading and has a suitable assignment operator for writing:
class X;
class Proxy {
X* object;
Key key;
public:
Proxy(X* object, Key key): object(object), key(key) {}
operator V() const { return object->read(key); }
void operator=(V const& v) { object->write(key, v); }
};
class X {
// ...
public:
V read(key) const;
void write(key, V const& v);
Proxy operator[](Key key) { return Proxy(this, key); }
V operator[](Key key) const { return this->read(key); }
// ...
};
Related
This question already has answers here:
C++ overloading array operator
(3 answers)
Closed 2 years ago.
I'm having some troubles trying to understand the following.
While writing a simple Matrix class with vector and templates I tried to overload the [] operator to acces the elements. This is the class
#pragma once
#include <vector>
#include <iostream>
template <typename T>
class Matrix
{
private:
std::vector<std::vector<T>> values;
int rows, cols;
public:
Matrix(int row, int col) : rows(row), cols(col) {
values = std::vector<std::vector<T>>(rows, std::vector<T>(cols));
}
std::vector<T>& operator[] (const int i) const{
return values[i];
}
};
Note that I'm returning a vector<T>, so I can use the double indexing easily.
The problem I'm having is that , when trying to call the operator, there is the following error:
Cannot realize the conversion from 'const_Ty' to 'std::vector<T,std::allocator<T>> &
And I have no clue where this conversion is happening. I'm not modifying anything from the class, so I thought I should use const.
The method being const means that the member variables are treated as const and can’t be modified.
You’re currently returning a std::vector<T>& which means a caller could modify it (but that goes against the const qualifier).
Change the return type to const std::vector<T>&, so that callers can’t modify the returned value.
I have a custom class with two overloaded brackets operators -- setter and getter. As you know they look somewhat like this
class IntContainer {
public:
int const & operator[] (size_t i) const;
int & operator[] (size_t i);
}
The problem I'm facing now, is that I have to check when the value was set or when it was just accessed, that is I need to track all the changes in my container. It's hard since always only non const operator is called, for example
container[i] = 3; // Non const operator[] called
x = container[i]; // Again, non const operator[] called
In two cases above I need to differ inner behavior in container. So is there any way to explicitly call different operators in cases like above. I don't want to use const instance of container and to define another functions like set and get, though I'm looking for smoe right design pattern.
Thanks!
One trick is to create a proxy object. This lets you overload the assignment operator and put your tracking logic into there and then you can guarantee that any writes are captured. If you have
class Proxy
{
int& val;
Proxy(int& val) : val(val) {}
Proxy& operator=(int new_val)
{
// do tracking stuff
val = new_val;
}
operator int() { return val; }
};
then you can adjust IntContainer to
class IntContainer {
public:
int operator[] (size_t i) const;
Proxy operator[] (size_t i);
};
and now you'll call the tracking code when the user actually tries to assign into the reference.
I have a project that wants me to make a BigNum class in c++ (university project)
and it said to overload operator bracket for get and set
but the problem is if the set was invalid we should throw an exception the invalid is like
BigNum a;
a[i]=11;//it is invalid because its >9
in searching I found out how to make the set work
C++ : Overload bracket operators [] to get and set
but I didn't find out how to manage setting operation in c# you easily can manage the set value what is the equivalent of it in c++
to make it clear in C# we can say
public int this[int key]
{
set
{
if(value<0||value>9)throw new Exception();
SetValue(key,value);
}
}
New Answer
I have to rewrite my answer, my old answer is a disaster.
The check should happen during the assignment, when the right hand side (11) is available. So the operator which you need to overload is operator=. For overloading operator=, at least one of its operands must be an user defined type. In this case, the only choice is the left hand side.
The left hand side we have here is the expression a[i]. The type of this expression, a.k.a the return type of operator[], must be an user defined type, say BigNumberElement. Then we can declare an operator= for BigNumberElement and do the range check inside the body of operator=.
class BigNum {
public:
class BigNumberElement {
public:
BigNumberElement &operator=(int rhs) {
// TODO : range check
val_ = rhs;
return *this;
}
private:
int val_ = 0;
};
BigNumberElement &operator[](size_t index) {
return element_[index];
}
BigNumberElement element_[10];
};
OLD answer
You can define a wapper, say NumWapper, which wraps a reference of BigNum's element. The operator= of BigNum returns the wrapper by value.
a[i]=11;
is then something like NumWrapper x(...); x = 11. Now you can do those checks in the operator= of NumWrapper.
class BigNum {
public:
NumWrapper operator[](size_t index) {
return NumWrapper(array_[index]);
}
int operator[](size_t index) const {
return array_[index];
}
};
In the NumWrapper, overload some operators, such as:
class NumWrapper {
public:
NumWrapper(int &x) : ref_(x) {}
NumWrapper(const NumWrapper &other) : ref_(other.ref_) {}
NumWrapper &operator=(const NumWrapper &other);
int operator=(int x);
operator int();
private:
int &ref_;
};
You can also declare the NumWrapper's copy and move constructor as private, and make BigNum his friend, for preventing user code from copying your wrapper. Such code auto x = a[i] will not compile if you do so, while user code can still copy the wrapped value by auto x = static_cast<T>(a[i]) (kind of verbose though).
auto &x = a[i]; // not compiling
const auto &x = a[i]; // dangerous anyway, can't prevent.
Seems we are good.
These is also another approach: store the elements as a user defined class, say BigNumberElement. We now define the class BigNum as :
class BigNum {
// some code
private:
BigNumberElement array_[10];
}
We need to declare a whole set operators for BigNumberElement, such as comparison(can also be done through conversion), assignment, constructor etc. for making it easy to use.
auto x = a[i] will now get a copy of BigNumberElement, which is fine for most cases. Only assigning to it will sometimes throw an exception and introduce some run-time overhead. But we can still write auto x = static_cast<T>(a[i]) (still verbose though...). And as far as I can see, unexpected compile-time error messages is better than unexpected run-time exceptions.
We can also make BigNumberElement non-copyable/moveable... but then it would be the same as the first approach. (If any member functions returns BigNumberElement &, the unexpected run-time exceptions comes back.)
the following defines a type foo::setter which is returned from operator[] and overloads its operator= to assign a value, but throws if the value is not in the allowed range.
class foo
{
int data[10];
public:
void set(int index, int value)
{
if(value<0 || value>9)
throw std::runtime_error("foo::set(): value "+std::to_string(value)+" is not valid");
if(index<0 || index>9)
throw std::runtime_error("foo::set(): index "+std::to_string(index)+" is not valid");
data[index] = value;
}
struct setter {
foo &obj;
size_t index;
setter&operator=(int value)
{
obj.set(index,value);
return*this;
}
setter(foo&o, int i)
: obj(o), index(i) {}
};
int operator[](int index) const // getter
{ return data[index]; }
setter operator[](int index) // setter
{ return {*this,index}; }
};
If what you are trying to do is overload [] where you can input info like a dict or map like dict[key] = val. The answer is actually pretty simple:
lets say you want to load a std::string as the key, and std::vector as the value.
and lets say you have an unordered_map as your underlying structure that you're trying to pass info to
std::unordered_map<std::string, std::vector<double>> myMap;
Inside your own class, you have this definition:
class MyClass{
private:
std::unordered_map<std::string, std::vector<double>> myMap;
public:
std::vector<double>& operator [] (std::string key) {
return myMap[key];
}
}
Now, when you want to load your object, you can simply do this:
int main() {
std::vector<double> x;
x.push_back(10.0);
x.push_back(20.0);
x.push_back(30.0);
x.push_back(40.0);
MyClass myClass;
myClass["hello world"] = x;
double x = myClass["hello world"][0]; //returns 10.0
}
The overloaded [] returns a reference to where that vector is stored. So, when you call it the first time, it returns the address of where your vector will be stored after assigning it with = x. The second call returns the same address, now returning the vector you had input.
This question already has answers here:
Erasing elements from a vector
(6 answers)
Closed 8 years ago.
I would like to remove an element from a vector. For example:
// object that is in the vector: MyClass obj;
// vector looks as so: vector<MyClass*> pVector;
pVector.remove(obj);
This will remove the object based purely on the pointer. Ideally, you would have comparison functions for your MyClass objects that actually check the objects to see if they are the same.
pVector.erase(std::remove(pVector.begin(), pVector.end(), obj), pVector.end());
Your question isn't well-defined, but I will provide you with two answers. I am assuming here based on your code that obj is not a pointer, which means that we are comparing an object to pointers to objects. This requires a custom functor.
The first answer is how to remove all elements of the vector where the value of the pointed-to element is equal to obj. This assumes that there is an operator== that can be applied to MyClass objects.
pVector.erase(std::remove_if(pVector.begin(), pVector.end(),
[&obj](MyClass * i) { return i && (*i == obj); }));
The second will remove at most one element, if it is found:
auto e = std::find(pVector.begin(), pVector.end(),
[&obj](MyClass * i) { return i && (*i == obj); });
if (e != pVector.end()) {
pVector.erase(e);
}
The lambda syntax requires C++11. If you don't have access to C++11 then you will have to build a functor by hand:
template <typename T>
class pointer_is_equal_to_object
{
public:
explicit pointer_is_equal_to_object(T const &);
bool operator()(T const *) const;
private:
T const & value;
}
template <typename T>
pointer_is_equal_to_object<T>::pointer_is_equal_to_object(T const & v) : value(v) {}
template <typename T>
bool pointer_is_equal_to_object<T>::operator()(T const * p) const
{
return p && (*p == value);
}
Then, for example, you could use:
pVector.erase(std::remove_if(pVector.begin(), pVector.end(),
pointer_is_equal_to_object<MyClass>(obj)));
Note that this complexity goes away if you stop using pointers and just use std::vector<MyClass>. Then your operator== can be applied directly and you can just do:
pVector.erase(std::remove(pVector.begin(), pVector.end(), obj));
Assuming C++11, and that you want to remove ANY elements matching obj, and not the exact obj... but you should be able to figure it out from here either way :)
http://en.wikipedia.org/wiki/Erase-remove_idiom
And for fun, here's an example: http://ideone.com/6ILYvo
#include <algorithm>
#include <vector>
std::vector<MyClass*> v;
MyClass * toberemoved = new MyClass();
//v gets populated...
auto itr = std::remove_if(v.begin(),v.end(), [&](MyClass* a){return *a == *toberemoved;});
v.erase(itr,v.end());
This question already has answers here:
Sorting a vector of custom objects
(14 answers)
Closed 9 years ago.
I have a vector<data> info where data is defined as:
struct data{
string word;
int number;
};
I need to sort info by the length of the word strings. Is there a quick and simple way to do it?
Use a comparison function:
bool compareByLength(const data &a, const data &b)
{
return a.word.size() < b.word.size();
}
and then use std::sort in the header #include <algorithm>:
std::sort(info.begin(), info.end(), compareByLength);
Just make a comparison function/functor:
bool my_cmp(const data& a, const data& b)
{
// smallest comes first
return a.word.size() < b.word.size();
}
std::sort(info.begin(), info.end(), my_cmp);
Or provide an bool operator<(const data& a) const in your data class:
struct data {
string word;
int number;
bool operator<(const data& a) const
{
return word.size() < a.word.size();
}
};
or non-member as Fred said:
struct data {
string word;
int number;
};
bool operator<(const data& a, const data& b)
{
return a.word.size() < b.word.size();
}
and just call std::sort():
std::sort(info.begin(), info.end());
Yes: you can sort using a custom comparison function:
std::sort(info.begin(), info.end(), my_custom_comparison);
my_custom_comparison needs to be a function or a class with an operator() overload (a functor) that takes two data objects and returns a bool indicating whether the first is ordered prior to the second (i.e., first < second). Alternatively, you can overload operator< for your class type data; operator< is the default ordering used by std::sort.
Either way, the comparison function must yield a strict weak ordering of the elements.
As others have mentioned, you could use a comparison function, but you can also overload the < operator and the default less<T> functor will work as well:
struct data {
string word;
int number;
bool operator < (const data& rhs) const {
return word.size() < rhs.word.size();
}
};
Then it's just:
std::sort(info.begin(), info.end());
Edit
As James McNellis pointed out, sort does not actually use the less<T> functor by default. However, the rest of the statement that the less<T> functor will work as well is still correct, which means that if you wanted to put struct datas into a std::map or std::set this would still work, but the other answers which provide a comparison function would need additional code to work with either.