4

let's say I have

class A {
  double a;
  double Value() const {
    return a;
  }

  double& Value() {
    return a;
  }

}

//later:
A foo;
double b = foo.Value();

now, the non-const version will be called. Is there a nice way to force the use of the const version? I think it's possible with a cast but I don't think it's very elegant.

lezebulon
  • 7,607
  • 11
  • 42
  • 73
  • 2
    The whole point of allowing overloads that differ only by `const` is to distinguish calls on a `const` object vs. a non-`const` object. If you don't want this behaviour, then don't define two overloads! (See http://www.parashift.com/c++-faq/const-overloading.html) – Oliver Charlesworth May 30 '13 at 17:03
  • Look at http://stackoverflow.com/questions/7287065/calling-a-const-function-rather-than-its-non-const-version – EGOrecords May 30 '13 at 17:04
  • @OliCharlesworth unfortunately I defined conceive the A class and I can"t change it – lezebulon May 30 '13 at 17:05
  • Why not delete the non-const version? – Jimmy T. May 30 '13 at 17:06
  • Elegant schmelegant. A cast is correct. – Benjamin Lindley May 30 '13 at 17:07
  • 1
    Why would you want to do this? If the two overloads have semantics that differ enough for this requirement to exist, then they should _not_ be overloads of one another. – Lightness Races in Orbit May 30 '13 at 17:13
  • @Lightness Races in Orbit : the issue is that I want to put a breakpoint inside the non-const function, and I'd like to be called only when the "a" member is going to be modified for sure. Right now the function is called 90% of the time just to read the value – lezebulon May 30 '13 at 17:15

5 Answers5

6

You may cast it to const.

double b = static_cast<const A&>(foo).Value();

(I don't think I've ever explicitly added const to a variable. I'm not sure if static_cast is more appropriate than const_cast.)

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • You are correct, by the way. `static_cast` is the correct cast to use. – GManNickG Jun 04 '13 at 18:06
  • Thanks for that verification, @GManNickG. – Drew Dormann Jun 04 '13 at 18:09
  • @GManNickG: Can you explain why `const_cast` is not the correct cast to use, when all you want to do is add constness? – Lightness Races in Orbit Feb 15 '18 at 01:30
  • @LightnessRacesinOrbit: It's about using the weaker cast. `const_cast` can remove const, so using it opens the door for error. That is to say, it's not *incorrect* to use it, but it's also not necessary. Since you can add const any time you'd like, `static_cast` is the weakest cast that lets you express that desire. You could imagine a `const volatile` variable is passed to a template function that wants to add const - `const_cast` would actually strip volatile away. I recognize that this is essentially epsilon away from purely theoretical. :) – GManNickG Feb 16 '18 at 07:50
  • @GManNickG: I disagree that `static_cast` is the weaker cast. By using it you are able to change the type in many more ways than altering its `const`ness. Conversely, `const_cast` cannot remove const when you are using it precisely because there is no const there. Although blergh regarding `volatile` fine but come on ^_^ – Lightness Races in Orbit Feb 16 '18 at 10:36
  • @LightnessRacesinOrbit: That's fair. :) Actually, I was just going to suggest the real best way would be to use `add_const` to acquire the correct target type, then use either static_cast or const_cast to get there - but then I just found out that C++17 added an explicit function for this! http://en.cppreference.com/w/cpp/utility/as_const I believe that would be the best way now. – GManNickG Feb 16 '18 at 15:53
3

A little prettier than casting -- assigning to a const reference doesn't require an explicit cast :

A foo;
const auto& cfoo = foo;
double b = cfoo.Value(); // will use Value()const

In practice, if you use const religiously in all your function arguments when you can, it is likely you are going to pass foo to bar(const A& x) at some point.

Mark Lakata
  • 19,989
  • 5
  • 106
  • 123
2

You can accomplish what you seem to want using a proxy:

class A {
  double a;

  class proxy { 
      A &a;
  public:
      proxy(A &a) : a(a) {}

      operator double() const { return a; }

      proxy operator=(double d) { a.a = d; return *this; }
  };

public:

  proxy Value() {
    return proxy(*this);
  }
}

// ...

double d = foo.Value();   // will use proxy::operator double.
foo.Value() = 1.0;        // will use proxy::operator=

This does require (internal) modifications to your class A, but not to code that uses it. It will, however, separate the code for reading vs. writing to the member data, so you can set breakpoints on each separately.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
1

You can't do that much elegant, but the least verbose would be do define

class A {
...

const A * operator->() const
{
   return this;
}

}

and use it

foo->Value();

instead of

foo.Value();

for const-version-only

kassak
  • 3,974
  • 1
  • 25
  • 36
0

Starting with C++17, we have std::as_const() which casts a reference to a const reference for you. For example:

#include <utility>
...
A foo;
double b = std::as_const(foo).value();
Daniel Schepler
  • 3,043
  • 14
  • 20