Sign in to follow this  
nlbs

a const method cant access non-const methods

Recommended Posts

To first order, you are correct. Think of a method as a function that takes an implicit first argument called `this', which is a pointer to the instance on which we are calling the method, and think of a const method as one where the `this' pointer is const. Things should be much more clear then.

Share this post


Link to post
Share on other sites
I've the following Code
const Var& Session::get(const string& key) const{
paramsMap::iterator it = item.find(key);//this line yields error
/*if(it != item.end()){
return item[key];
}*/

const Var& d(0x0);
return d;
}

the above mentioned line yields error.
item is a string (non-const) member variable of the Session Class
paramsMap is a typedef of map<string, Var>
this is the error message shown
error: conversion from
‘std::_Rb_tree_const_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Var> >’
to non-scalar type
‘std::_Rb_tree_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Var> >’ requested

Share this post


Link to post
Share on other sites
Replace paramsMap::iterator with paramsMap::const_iterator.

Const-correctness rules apply for iterators as well -- iterator is just a kind of pointer (or rather, saying that pointer is a special case of iterator would be more correct). If you have a const object, item, you can't make a non-const iterator to it, because it would allow you to modify the object through that iterator. Const iterators don't allow you modification of the object they point to. And yes, item is const in this case, because .get() is a const function.

Share this post


Link to post
Share on other sites
If I uncomment this Block
the
item[key]
yields error
const Var& Session::get(const string& key) const{
paramsMap::const_iterator it = item.find(key);//this line is ok now
if(it != item.end()){
return item[key];//This line yields error
}
const Var& d(0x0);
return d;
}



But I am not inserting an Item.
error: passing ‘const paramsMap’ as ‘this’ argument of
‘_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&)
[with
_Key = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, _Tp = Var,
_Compare = std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >,
_Alloc = std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >,
Var> >]’ discards qualifiers

Share this post


Link to post
Share on other sites
Quote:
Original post by nlbs
If I uncomment this Block
the
item[key]
yields error
*** Source Snippet Removed ***
But I am not inserting an Item.
[code]error: passing ‘const paramsMap’ as ‘this’ argument of ‘_Tp& ...

You can't use operator[] in a constant map, because operator[] might want to insert a default-constructed value for your key. Just return it->second.

Share this post


Link to post
Share on other sites
Quote:
Original post by nlbs
But I am not inserting an Item.


Yes, you are. operator[] inserts an item if it does not exist yet, which means that it can potentially change the map. Use it->second instead.

Share this post


Link to post
Share on other sites
In regards to your current error:

return item[key];

Operator [] for maps is not a constant operation. The reason is, if 'key' does not exist in the map, it will add a new default constructed element with 'key' as the key. Why not just use the iterator you found above to return the value instead of using find, getting an iterator, then looking up the item again based on the key (which is what item[key] does).

return it->second;

Should work with a const iterator.

Share this post


Link to post
Share on other sites
Please be aware of posting long text lines (particularly template-related error messages). They break the forum, making the thread obnoxious to read. I've corrected the formatting on the offending posts.

In the future, use hard newlines at logical points during the error to prevent the issue.

Share this post


Link to post
Share on other sites
Ahhhhhhhh!
I got it operator[] is a non-const operation
so I used return it->second Instead on that line.

and no if I used return Var(0x0) that was a reference to local variable but
const Var& d(0x0);
return d;
doesnt fire this Error
and as its returning a const Var& is there any other options ??
cause I want to return a blank const Var()'s reference

Share this post


Link to post
Share on other sites
Quote:
Original post by nlbs
cause I want to return a blank const Var()'s reference


Then, you must find a blank value which will remain alive after the function returns. Which, in your case, won't happen since it's a local variable. You may wish to create a member variable of your class and return a reference to that instead.

Share this post


Link to post
Share on other sites
Quote:
Original post by nlbs
[...]and no if I used return Var(0x0) that was a reference to local variable but
const Var& d(0x0);
return d;
doesnt fire this Error
and as its returning a const Var& is there any other options ??
cause I want to return a blank const Var()'s reference

You managed to fool the compiler, but that doesn't mean that the code is correct. There's no such thing as a blank reference. You can return a pointer instead of a reference, and then you can use 0 to represent "blank".

EDIT: You can have a global variable (or static member, to be cleaner) with a default-constructed Var, which you can then return a reference to to represent "blank".

Share this post


Link to post
Share on other sites
But If I use
const Var& d(0x0);
return d;
g++ doesn't throw any warning/error like returning reference to local variable.
Edit: If I use return 0; compiler fires an warning message saying returning reference to temporary cause the return type is not a pointer.

Share this post


Link to post
Share on other sites
Quote:
Original post by nlbs
But If I use
const Var& d(0x0);
return d;
g++ doesn't throw any warning/error like returning reference to local variable.


This means the compiler is not smart enough to see what you are doing wrong, nothing else.

Share this post


Link to post
Share on other sites
Quote:
Original post by nlbs
g++ doesn't throw any warning/error like returning reference to local variable.


Yes, this happens a lot in C++: you write incorrect code and the compiler doesn't warn you about it. We, however, are smarter than the compiler. And we're giving you that warning: this returns a reference to a local variable.

Share this post


Link to post
Share on other sites
Quote:
Original post by nlbs
But If I use
const Var& d(0x0);
return d;
g++ doesn't throw any warning/error like returning reference to local variable.
Edit: If I use return 0; compiler fires an warning message saying returning reference to temporary cause the return type is not a pointer.


This is because, while it is not a syntax error (which is what would give you a hard error) it does result in undefined results. I have seen compilers that do warn in this situation, but there is no standard for what compilers should give you warnings for and what they shouldn't. Either way, what you are doing is wrong and will cause issues at some point if you don't fix it. Your best bet is to return a pointer like Alvaro suggests, since you can return a null pointer but there is no such thing as a valid null reference in C++ (yes, I am aware that you can hack around stuff and set a reference to null, but you can't really use it).

Share this post


Link to post
Share on other sites
so whats the difference between
Var x;//x is a variable of type Var holding an Instance of Var in it

and
Var& x;//x is a local variable which holding a reference of an Instance of var in it
//e.g. when I am using return x its returning a local variable itself
//However the variable may or may not be a reference internally

so when I am returning the second x Its returning a variable not a reference to a variable. cause the return type is Var& also.
but when I am returning the first x its returning the reference to the local variable x as the return type is Var&.

So the First case we should get an warning and not in second case.

am I right ??

Share this post


Link to post
Share on other sites

In both cases if you return a Var&, what you are doing is wrong. The first case is just more obvious to the compiler for whatever reason, so it catches it. It *should* warn you about the second case if it warns you about the first, but for some reason it doesn't catch it.

Let me put it this way. A reference in C++ is nothing but a pointer internally. It holds the address to the object it is referencing, and just spares you from having to dereference with * or ->. When you return a reference to a local variable of a function, as you are doing, you are returning a pointer to that variable. As soon as that function returns, any local variables of that function go out of scope. This means that their destructor is called, and the stack space they used is available for use by other stack variables, function calls, whatever. But now, you have returned a pointer to that memory which can now be used by something else. If you then try to use that memory (by performing some operation on the returned reference), do you see where the issue comes from?

Share this post


Link to post
Share on other sites
Quote:
Original post by nlbs
so whats the difference between
*** Source Snippet Removed ***
and
*** Source Snippet Removed ***
am I right ??


No, you are wrong in the case you posted. It does not matter how you create the local variable, your function sig is:

const Var& Session::get(const string& key) const{

which means it returns a Var&.

Var x;
This creates a local stack variable 'x' which goes out of scope when the function returns.

Var& x;
This alone should not compile, as a reference in C++ must be assigned to something.

const Var& d(0x0);
This should not compile either, unless your Var type has a constructor that takes an integer and creates a Var. In this case, what you did is equal to doing:

const Var& d = Var(0x0);

Which again is just creating a local temporary variable on the stack that will go out of scope as soon as the function returns (or sooner since it is just a temp variable in this case).

Here is a simple program that, when run in debug mode in VC++EE shows the problem. The reason it shows the problem is that VC fills uninitalized variables, and freed stack space with 0xCC in every byte. This means that you can catch issues where you are using uninitalized variables, or other invalid constructs rather easily.


#include <iostream>
using namespace std;

class Var
{
int y;
public:
Var(int x):y(x){ }
void print() const { cout << y << endl; }
};

const Var& test()
{
const Var& v(0x6);
v.print(); // 6 here
return v;
}

int main()
{
test().print(); // 0xCCCCCCCC here
return 0;
}




This program prints:
6
-858993460

This is because, while still in the function the call to Var::print on the local variable is valid, and it prints 6. As soon as the function returns, the stack variables go out of scope and debug mode fills it with 0xCC. This means, when you try to call Var::printon the return value from test(), it now prints -858993460, which corresponds to a signed 0xCCCCCCCC. This is the same problem your program will run into, although with a compiler that does not fill unused space with garbage it would be much harder to detect as the values you expect to be at that memory address might still be there if nothing new has been pushed onto the stack yet. Just because they are there does not make it safe though.

Note, VC++05 does not flag this code with a warning either, although it is clearly incorrect.

[Edited by - Dranith on July 28, 2008 12:32:49 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this