I'm overloading the subscript operator for the first time and I'm having troubles in returning a reference value.
I followed the rules of thumb from a post in c++faq tag, but there's something I'm missing.
const T& operator[](int index) const {
if ((index < 0) || (index > size)) {
// provide exception handling for this error
std::cout << "Error! Index out of bound." << std::endl;
std::exit(0);
} else {
Block* b = head;
while (b) {
if (b->position == index)
return *b->data;
b = b->next;
}
}
}
I implemented them in both variants: with const return value and const function (as above), and without (that is identical except for the two const keywords).
The problem is that when I run the test main, it simply crashes. I thought that the bug was in the return *b->data; statement, but I can't figure neither which it could be nor if I'm wrong and there are other errors.
Any ideas?
Thanks in advance.
If you want to return a reference on data, I'm not sure if it's what you want, you to return a reference of type T and I'm assuming data is of type T, it should be something like:
return b->data;
Else, you are returning a reference on the adress of data.
EDIT: Corrected a mistake
Related
Please consider this scenario.
I'm creating a function for [] operator as in the std::vector whose inner body is like this:
int temp;
if(argument >= 0 && argument < size) {
return &my_vector[i];
} else {
cout << "out of bounds" << endl;
//i need to return here something but this gives me an error: local variable cannot be returned by reference.
return &temp;
}
where argument is the argument passed to the function. The return type for this function is 'T*'.
I'm looking for both the read and write cases:
vector[100] = 1;
and int answer = vector[100];
That's why they are returned by reference.
What should be returned in the else part?
First of all, you are not returning a reference, but a pointer, which makes the method not very useful. Instead of
vector[100] = 1;
int answer = vector[100];
You would have to write
*vector[100] = 1;
int answer = *vector[100];
To get what you want you should return a reference not a pointer. Ie return type should be T& not T*.
Then you have basically two options. Either you mimic vectors operator[]. It does not do any bounds checking. It is up to the caller to make sure to pass valid indices:
return my_vector[i]; // thats all (assuming my_vector[i] returns a reference)
The other option is to throw an exception as std::vector::at does:
if(i >= 0 && i< size) {
return my_vector[i];
} else {
throw std::out_of_range("invalid index");
}
You cannot return a reference to a local variable, because that reference is dangling as soon as the method returns and the local variables lifetime ended.
You have to choose which approach fits better. std::vector offers both. Typically when the method is called in a loop:
for (size_t i=0; i<size;++i) {
foo[i] = i;
}
You do not want to incur the overhead of checking the index on each access. On the other hand sometimes indices are computed and the caller wants to have the bounds check encapsulated rather than doing it manually:
try {
size_t index = some_function(); // is this a valid index?!?
foo.at(i) = 42; // let at check it
} catch(...) {
//...
}
PS: You typically need two overloads for those accessors, one const and one non-const, returning const T& and T&, respectively.
I am implementing a member function called CurrentUser. It will take a username as parameter and return the User instance object which matches the given username. Below is the code
User& UserDB::currentUser(string username){
// userlists is a instance member which is list of user objects
for(list<User>::iterator i = userlists.begin(); i != userlists.end(); ++i)
{
if(*i.getName().compare(username)==0){
return *i;
}
}
return null;
}
Not sure if it is the correct way to do so. Correct me if it is wrong. Thanks!
Update:
hey guys thanks for your advice, i figure out a way to do so by returning a User pointer. Here is the code.
User* UserDB::currentUser(string username){
for(list<User>::iterator i = userlists.begin(); i != userlists.end(); ++i)
{
if(i->getName().compare(username)==0){
return i;
}
}
return null;
}
there are a couple of ways to do this cleanly.
You can of course return a pointer, but that will be surprising to people as it is more normal to return a reference or an object. pointers present object consumers with a number of problems, e.g.:
what should I conclude if it's null?
should I delete it?
and so on.
Returning a reference to something or a something removes these ambiguities.
Having said that, references cannot be empty, so the function must return something. If it does not find the item it's looking for, it must indicate that to the caller. One way is an exception (i.e. it was logically incorrect to ask for that item). However, if the item not being there is a normal occurrence, then you don't want to force your consumers to handle exceptions - that's bad form too.
So the answer is to return an object that encapsulates an optional reference.
A good example of this is boost::optional<User&> but if you don't want to include boost it's fairly simple to roll your own:
struct optional_user
{
using element_type = User;
using reference_type = element_type&;
optional_user() : _p(nullptr) {}
optional_user(reference_type r)
: _p(std::addressof(r))
{}
bool valid() const { return bool(_p); }
// compares to true if the user is present, false otherwise
operator bool() const { return valid(); }
reference_type value() const {
assert(_p);
return *_p;
}
// can be used anywhere a User& is required
operator reference_type () const {
return value();
}
private:
element_type* _p = nullptr;
};
now your function becomes:
optional_user UserDB::currentUser(string username)
{
typedef list<User>::iterator Iter;
for(Iter i = userlists.begin(); i != userlists.end(); ++i)
{
if(i->getName().compare(username)==0)
{
return optional_user(*i);
}
}
// return an indicator that the user is not present
return optional_user();
}
and your call site becomes:
optional_user = users.currentUser("bob");
if (optional_user) {
do_something_with(optional_user /* .value() */);
}
If you want to specifically return a reference, the item you are referring to must have a lifetime after execution leaves the function.
There are several alternatives:
static local variable in function
using dynamic memory
variable declared outside function
Pass by non-const reference
Here is an example of #1:
const std::string& Get_Model_Name(void)
{
static const std::string model_name = "Accord";
return model_name;
}
Other alternatives are to return a variable by value (copy). This doesn't use references. A copy is returned.
For example:
std::string Get_Manufacturer_Name(void)
{
return std::string("Honda");
}
You may also consider passing by parameter and modifying the parameter:
void Get_Lunch_Special_Name(std::string& entree_name)
{
entree_name = std::string("Beef Wellington");
}
What is the proper thing for me to return here?
char BCheckString::operator[](int index)
{
if (index < 0 || this->length() <= index)
{
throw IndexOutOfBounds();
???Do I need to return something here???
}
else
{
return ?????;
}
}
I tried return this[index] but VS2013 says: "no suitable conversion function from "BCheckString" to "char" exists. And I have no idea what to return after the throw.
I have:
class BCheckString : public string
{
private:
bool checkBounds();
public:
BCheckString(string initial_string);
char operator[](int index);
class IndexOutOfBounds{};
};
and
BCheckString::BCheckString(string initial_string) : string(initial_string)
{
}
char BCheckString::operator[](int index)
{
if (index < 0 || this->length() <= index)
{
//throw IndexOutOfBounds();
cout << "index out of bounds" << endl;
return 'A';
}
else
{
return 'A';
}
}
Obviously this is homework ;)
While observing that what you're doing here is unnecessary, the syntax is thus:
return string::operator[](index);
You're calling the operator[] of your string parent. This should be preferred to using c_str because string::operator[] does bounds checking in debug builds.
It's also worth noting that .at already does bounds checking in release builds, and throws std::out_of_range.
For the first question, no. After throwing an exception, you don't need to have a return statement. In fact, if you do, the compiler may warn you about "unreachable code".
First, deriving from std::string is not recommended: Why should one not derive from c++ std string class?
As to your questions:
1) After throw you don't return anything.
2) Your attempt to use operator[] is incorrect as you are not calling the parent class's std::string::operator[].
To call the correct operator[]:
else
{
return std::string::operator[](index);
}
this is a pointer, therefore this[index] will erroneously consider this as pointing to an array of instances to access the index-th of them. That will be an instance of the class itself and there's no implicit conversion from that to the declared return type char (this is what the error message is complaining about).
You need to get the char from the base string, and this is done with
return this->string::operator[](index);
If you derived from std::string you can just use c_str() method to access data.
return this->c_str()[index];
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.
I'm writing a class that wraps a dynamically allocated array and I'm trying to write the operator[] function. Currently I have:
bool& solution::operator[](unsigned int pos)
{
if(pos < _size)
{
return this->_data[pos];
}
else
{
return false;
}
}
But I get the following error from g++:
error: invalid initialization of non-const reference of type ‘bool&’ from an rvalue of type ‘bool’
How should I be doing this? I need the [] operator to be able to modify elements.
Its because the boolean literalfalse which is a rvalue, cannot be bound to non-const reference bool& which is the return type of operator[].
Simply change the return type from bool& to bool, the error will disappear. But that isn't going to solve your problem, as you said,you want to return reference of the element, so that the element can be changed on the callsite, then you've to something like this:
//correct solution
bool& solution::operator[](unsigned int pos)
{
if(pos > _size)
throw std::out_of_range("invalid index");
return this->_data[pos];
}
That is, you should notify the caller of an invalid index, so that it can know that something went wrong. C++ various exception classes are there exactly for that purpose, i.e to notify error.
Attempting to return any value (false or true) when the index is invalid, simply hides the problem. Ask yourself, if you return a dummy boolean value (which you store in the class), then would the caller know if the index was invalid? No.
//incorrect solution
bool& solution::operator[](unsigned int pos)
{
if(pos > _size)
return _dummy; //it hides the problem, no matter if its true or false!
return this->_data[pos];
}