I have done numerous searches and found a ton of examples and tutorials but still cannot figure out how to get the value when writing to the [] operator...
I feel like i'm going insane. I must be missing something very simple
as far as i can tell there is a single function for get and set and it looks something like this:
V& operator[](string K);
or this:
double &operator[](int n);
now great, we can get what is:
a[HERE]
as HERE becomes double &operator[](int HERE);
and we can easily work with it
but how do we get what is:
a[4] = HERE
C# has two very clear get and set methods with the value keyword which represents the object being assigned.
public string this[int key]
{
get
{
if(key == 1)
return "1!";
if(key == 2)
return "2!";
else
return "3!";
}
set
{
if( value == "setting") //value is a[3] = THIS
this.isSet = true;
}
}
Don't think of operator[] as a function to get or set, that might be confusing. Think of it as a normal function. In fact, let's re-write it as a normal function:
struct X
{
int arr[10];
int& getIndex(int idx) { return arr[idx]; }
};
//...
//initialize x of type X
x.getIndex(3) = 42;
The method x.getIndex(3) will return a reference to the 4-th element in the member array idx. Because you return by reference, the return value is an l-value and refers to that exact element, so you can assign a value to it, say, 42. This will modify the member x.arr[3] as the function returns an alias for that particular object.
Now you can re-write this in terms of operator[] exactly as before
struct X
{
int arr[10];
int& operator[](int idx) { return arr[idx]; }
};
and get the same result by calling
x[3];
or even
x.operator[](3);
The operator option is just syntactic sugar.
Related
So I have a function which sets a variable in a vector and returns a modifiable cell reference back. But I'm unsure if I am using the reference '&' correctly as I have two examples that work.
Ex1:
Cell& Grid::set(const int x, const int y, const Cell & value) {
int index = get_index(x, y);
this->grid[index] = value;
return this->grid[index];
}
Ex2:
Cell& Grid::set(const int x, const int y, const Cell value) {
int index = get_index(x, y);
this->grid[index] = value;
return this->grid[index];
}
Which would be the correct way and how can I tell for the future?
Edit: Cell is an enum not an object
This is a sink function for the value parameter, because of this:
grid[index] = value;
So in this case, you should be passing by non-const value and move it into grid:
Cell& Grid::set(const int x, const int y, Cell value)
{
grid[get_index(x, y)] = std::move(value);
return grid[index];
}
You should make sure that Cell is a movable type though, because if it isn't, it will cost you an extra copy.
This is the common pattern that is used when it comes to sink functions. (Meaning functions that store arguments somewhere that outlives the function call itself.)
Also take a look at another relevant answer to a different question on when it's beneficial to pass by reference.
I have a class Point which has a member method to get position:
class Point {
private:
int x; int y;
public:
Point(int a, int b) {
x = a; y = b;
}
int getX() { return x; }
int getY() { return y; }
};
These are stored in a list<Point> named listPoints. I have a function which checks whether a position matches any of the points in the list:
bool checkMatch(int x, int y) {
for (Point p : listPoints) {
if (p.getX() == x && p.getY() == y) {
return true;
}
}
return false;
}
Note the . is used to access member methods of Point, but there's another way:
bool checkMatch(int x, int y) {
list<Point>::iterator p = listPoints.begin();
for (; p != listPoints.end(); ++p) {
if (p->getX() == x && p->getY() == y) {
return true;
}
}
return false;
}
What is this function doing differently to the one before, specifically why does . no longer work and I need to use -> instead to access member methods of Point? Are these foreach loops fundamentally different?
They're not different no, with some very minor exceptions. In the second loop, you're using an iterator, which is more-or-less a pointer to the object itself. It can be dereferenced to get the actual object.
You'd use iterators if you wanted to remove some elements. So say instead of checking for matches, you were removing anything that matched, you'd want to iterate with iterators.
Since you are just iterating over the entire range, it's far clearer to use your for-ranged loop. It's easier to write and clearer.
specifically why does . no longer work and I need to use -> instead to access member methods of Point?
Because the iterator is an object, which basically points to the actual object. You cannot override the dot operator, so instead operator-> is overridden to retrieve the object. One could also dereference the iterator like *p, which allows you to use the dot operator (*p).getX()
Are these foreach loops fundamentally different?
They are not fundmentally different. They are subtly different.
It's analogous to:
int a;
int* ptr = &a;
a = 10;
*ptr = 10;
The last two lines are not fundmentally different. An iterator is kinda like a pointer. Its operator* is overloaded such that using *p acts as though you are dereferencing a pointer -- you get a reference to an item in the container.
The second block of code can be changed a bit to resemble the first one.
list<Point>::iterator iter = listPoints.begin();
for (; iter != listPoints.end(); ++iter) {
Point& p = *iter;
if (p.getX() == x && p.getY() == y) {
return true;
}
}
Under the covers, the first block is exactly that.
See the documentation on the range-for loop in the standard for the details.
I want to define s[i] to return 0 if s[0] was never assigned and return a reference to s[i] if s[i] was assigned earlier (to implement a sparse array). The following code does it, but it ends up creating s[i] whenever I try to get its value, because of the semantics of map.
struct svec{
map<int,double> vals;
/*
double operator[](int index){
return (vals.count(index) > 0) ? vals[index] : 0 ;
else return 0;
}
*/
double &operator[](int index){
return vals[index];
}
};
int main(){
svec s;
s[0] = 10;
cout << s[1] << endl;
}
I want the commented code to be used for resolving the expression s[1]. But if I uncomment it, I get an error.
You cannot overload return values, so you'll have to stick with either returning by reference or by value (or by pointer, etc). The problem with returning by reference is that you have to refer to an existing value that lives in memory. This is, of course, fine when the value is in the map. When it's not, you have to create the default value and store it in memory. Then you have to make sure to properly delete it to not leak memory, but also to make sure the user isn't holding references to the values, as it would introduce unexpected behaviour.
Also, you have to consider the fact that the user can change the value you're returning. If you return the same default, then it's possible for the user to change it to another value. Then all subsequent calls would return a reference to the new value. Resetting the default to 0 every time you return it would also be unexpected for all users that are still keeping a reference to it.
You probably could solve this problem in a stable way, but it would probably require much boilerplate code. I would suggest putting the burden on the user in this case.
class SparseVector {
private:
std::unordered_map<int, double> elements;
public:
void set(int index, double value) {
elements[index] = value;
}
double& get(int index, double& optional) {
auto it = elements.find(index);
if (it != elements.end())
return it->second;
else
return optional;
}
double& get(int index) {
auto it = elements.find(index);
if (it != elements.end())
return it->second;
throw std::runtime_error(
"Couldn't find element at index " + std::to_string(index) +
"! Use get(int index, double& optional) if you don't want errors."
);
}
}
int main() {
double default_value = 0.0;
SparseVector vector;
std::cout << vector.get(0, default_value) << std::endl;
}
this is the first time I've done something like this so I'm a little uncertain how I need to do this. I have a very simple class which contains some simple values and some getters:
class Nucleotide{
private:
char Base;
int Position;
int Polymorphic;
public:
Nucleotide(char ch, int pos);
int getPos();
char getBase();
int getPoly();
};
This class is present in another class that contains a vector of them:
class NucleotideSequence{
private:
std::string Name;
std::vector<Nucleotide> Sequence;
public:
NucleotideSequence(std::string name, std::vector<Nucleotide> seq);
std::string getName();
Nucleotide getBase(int pos1);
};
I want the method of the second class called getBase to be able to take a integer - say 1, and return the first Nucleotide object in the vector. What I've written is below:
Nucleotide NucleotideSequence::getBase(int pos1)
{
for(std::vector<Nucleotide>::iterator i = Sequence.begin(); i != Sequence.end(); i++)
{
if(pos1 == (*i).getPos())
{
return i; // Return a pointer to the correct base.
}
}
}
I've got Nucleotide as the return type but I was wondering really how I should change this - since if I return nucleotide because of pass by value would it not just return a copy of the object at that place in the vector? So I'd rather return a pointer/reference. I'm using an iterator in the loop so should I just return a pointer with the value of the iterator? How do I do this? In the function I return i but should I be returning i&? I'm uncertain about the specifics - presumably if I'm returning a pointer my return type needs to be Nucleotide* or perhaps Nucleotide& since & means address of? I've thought this through and read Cpp tuts but I'm still slightly unsure of the right answer.
Thanks,
Ben.
You have to return the Nucleotide by reference:
Nucleotide & NucleotideSequence::getBase(int pos1)
{
for(std::vector<Nucleotide>::iterator i = Sequence.begin(); i != Sequence.end(); i++)
{
if(pos1 == (*i).getPos())
{
return *i; // Notice the *i instead of i
}
}
}
A reference works very similarly to pointer (allows you to pass the actual object, not its copy), but cannot be null and cannot point to non-existing object, so it's a lot safer than pointer.
Note though, that if you don't find the desired Nucleotide, you don't return anything, what generally is not a good idea. In this case using pointers may actually be a better idea:
Nucleotide * NucleotideSequence::getBase(int pos1)
{
for(std::vector<Nucleotide>::iterator i = Sequence.begin(); i != Sequence.end(); i++)
{
if(pos1 == (*i).getPos())
{
return &(*i);
}
}
return nullptr;
}
You don't return a pointer, you attempt to return the iterator. And the function is declared to return an instance and not a pointer. Also, if you don't find the Nucleotide you don't return anything at all leading to undefined behavior if you try to use the "returned" value.
You could change the function to return a pointer, or a reference, or just a by value (copying like it's declared like not.
You can also change so that the function takes the Nucleotide as an argument instead, and then return a boolean indicator if it was found or not.
bool NucleotideSequence::getBase(int pos1, Nucleotide& n)
{
for (...)
{
if (...)
{
n = *i;
return true;
}
}
return false; // Not found
}
As far as your question is concerned, returning a reference (&) as suggested by others is the solution.
In order to improve your code, I would as well suggest a change:
Either go for the operator[], or use the at() present in std::vector.
Thus, you can directly say:
return Sequence[pos1]; or return Sequence.at(pos1);
Your code will benefit from some use of references for efficiency's sake. The getBase method signature should look like this:
const Nucleotide& NucleotideSequence::getBase(int pos1)
The NucleotideSequence constructor signature should look like this:
NucleotideSequence(const std::string& name, const std::vector<Nucleotide>& seq);
And the getName method like this:
const std::string& getName();
(Although return value optimisation might make that less important.)
As for the contents of getBase, it might help understanding to break down the code into:
const Nucleotide* NucleotideSequence::getBase(int pos1)
{
for(std::vector<Nucleotide>::iterator i = Sequence.begin(); i != Sequence.end(); ++i)
{
Nucleotide& ref = *i; //Get a reference to the object this iterator points to
if(pos1 == ref.getPos()) //compare its base to the argument
{
return &ref; // Return a pointer to the correct object.
}
}
return NULL; //or null if we didn't find the object we wanted
}
Sometimes when I'm programming in C++ I wish there was an undefined value for every variable something like Javascript!.
For example when I'm returning a value for out-of-bounds element of an array, it was useful to return an undefined instead of throwing an exception, or:
template <typename T, int SIZE>
class MyArray
{
T arr[SIZE];
static T badref;
public:
T &operator[](int i)
{
if (i >=0 && i < SIZE)
return arr[i];
else
throw std::string("OUT-OF-BOUNDS"); // or: return badref; !!
}
};
Another dirty(In my opinion) option is returning a reference of a pre-defind variable as a bad-reference variable. I know we can not assign null or something like that to a reference variable.
Is there an another well formed pattern to return a reference where caller has the ability to find out the returned value is not valid?
EDIT: I'm not mean a pointer
You can use boost::optional as #chris mentioned in his comment. It comes as a part of Boost libary. See this page for more details.
Modified MyArray class:
template <typename T, int SIZE>
class MyArray
{
T arr[SIZE];
public:
optional<T&> operator[](int i)
{
if (i >=0 && i < SIZE)
return optional<T&>(arr[i]);
else
return optional<T&>();
}
};
Usage:
MyArray<int>() array;
// fill array with data
optional<int&> result = array[0];
if (result) {
// item was found
} else {
// index out of bounds
}
I wish there was an undefined value for every variable something like Javascript!
You only have an "undefined" value for pointers (nullptr). A reference is (by definition) something pointing to a valid instance.
To return a reference to a static object, you should separate between const and non-const values of your operator:
template <typename T, int SIZE>
class MyArray
{
T arr[SIZE];
static T badref;
public:
T &operator[](int i)
{
if (i >=0 && i < SIZE)
return arr[i];
else
// returning ref here would allow clients to write:
// MyArray<int> a;
// a[-1] = 5; // valid if you return a non-const reference
throw std::string("OUT-OF-BOUNDS");
}
const T &operator[](int i) const
{
if (i >=0 && i < SIZE)
return arr[i];
else {
// MyArray<int> a;
// a[-1] = 5; // will not compile (cannot assign to const)
static const T invalid = T();
return invalid;
}
}
};
Whatever you think of, your solution needs to fit into the type system. So your function signature must explicitly say (this way or another) that the result may be T, but it may be something else too.
Common ways for that are:
Instead of returning a value, return a status code and output the value via an "out" parameter (a pointer or reference):
bool tryGet(int i, T& result);
Return a tuple (status, value) like:
std::tuple<bool, T> get(int i)
(If couldn't get, consider the second tuple element irrelevant - requires T to have a default constructor)
Use boost::variant (flexible, but requires boost)
Use boost::optional (simpler version of the above, when you only need "either T or nothing")
The main goal of references is to avoid invalid (NULL) values while allowing functions to modify their arguments and keep from copying data. If you need a NULL value, use a pointer.