tags:

views:

176

answers:

5

This is what I'm trying to do and I can't:

#include <string>
using namespace std;
class A {
public:
  bool has() const { return get().length(); }
  string& get() { /* huge code here */ return s; }
private:
  string s;
};

The error I'm getting is:

passing ‘const A’ as ‘this’ argument of
‘std::string& A::get()’ discards qualifiers

I understand what the problem is, but how can I fix it? I really need has() to be const. Thanks.

+6  A: 

Add a second overload of get():

string const & get() const { return s; }

That will be called on a const typed object of class A.

In practice, I prefer adding only const-typed accessors, and then keeping modifications entirely internal to the class or even avoid them entirely. For example, that means having a method DoUpdateLabel(){/*do something with s*/} rather than expose the internals. That has the nice side effect that you can avoid duplicating accessors in many cases.

If you absolutely must have modification via accessors and you also don't want an extra const wrapper, you can use const_cast<>:

bool has() const { return const_cast<A*>(this)->get().length(); }

However, if get() has side-effects and has() is declared const, it's questionable whether this is behavior you really want.

Eamon Nerbonne
No; **change** get(). I can't think of a reason why you'd want to write `a.get() += "abc"`.
tc.
:-) - agree completely - that's exactly what I was just adding as an edit!
Eamon Nerbonne
I can't have two versions of `get()` because my function (in the real-life project) is rather big. I don't want to duplicate the code.
Vincenzo
`a.get() += "abc"` is exactly the case
Vincenzo
Your problem can be solved - but modifying internal state via accessors is a maintenance headache. If you can post a more realistic example, perhaps I can find a cleaner solution...
Eamon Nerbonne
`const_cast` does the trick, many thanks!
Vincenzo
+2  A: 

It's pretty usual to have const and non-const versions of the same functions.

So, one of them would be called if your object is called as a const object, and the second - if the same happens with a non-const.

I have to mention, that this technique usually is used with operators but is also suitable for you current case.


Also, in your current case the best approach could mean having ONLY const method like

const string& get() const { return s }

and refactoring all your other code so it works with a string returned via constant reference.

Kotti
+1: we're obviously on the same message here :-)
Eamon Nerbonne
Vincenzo
A: 
std::string get() const { return s; }

because passing strings by value is usually cheap (I don't remember if this is a guarantee), and I don't think you want the string you're looking at to change under your feet when A modifies it.

tc.
`string` is just an example. in the real-life project I have another class and another purpose of the code
Vincenzo
Also, copying even small strings is actually quite expensive due to the memory (de-)allocation overhead. C++0x move constructors might fix that...
Eamon Nerbonne
AFAIK, GCC/libstdc++'s std::string is backed by a reference-counted buffer, giving it copy-on-write-style behaviour: Copying is O(1) and mutating is cheap provided the refcount is 1. While it's not a good idea to rely on implementation details, it's not the end of the world either.
tc.
A: 

Try this:

const string& get() const { return s; }

The first const applies to the return type of the method get(). The return type is a reference to a string. Altogether it's a const reference to a string, which enforces that this returb value is not to be changed.

The second const applies to the method, effectively making it a const method.

Khnle
+2  A: 

Hi,

I think your question is a bit vague.

You have:

bool has() const { return get().length(); }
string& get() { /* huge code here */ return s; }

... and need has() to be const.

I can think of three ways to get around this, depending on what you're actually trying to do:

The cleanest option would be for has to only use const code. If some of your /* huge code here */ code non-const but doesn't actually change the logical value of the class (like computing a cached internal value) consider using mutable on the data involved.

If the /* huge code here */ part is non-const by nature, consider refactoring it into a different function and calling it separately:

using namespace std;
class A {
public:
  bool has() const { return get().length(); }
  const string& get() const {  return s; }
  void computestuff() { /* huge code here */ }
private:
  string s;
};

A  instance;

// old client code:
// string s = instance.get();
//
// new code:
instance.computestuff(); // non-const call
instance.get(); // const call
instance.has(); // const call

Ultimately, you can also cast the const-ness away (but keep in mind that the need to do so is almost always a sign of bad design/need to refactor):

class A {
public:
  bool has() const { return const_cast<A*>(this)->get().length(); }
  string& get() { /* huge code here */ return s; }
private:
  string s;
};
utnapistim
This would be my *preferred* solution as well - though it doesn't look like the OP's leaning in that direction.
Eamon Nerbonne