Having fun with 'const'

Here is something all the c++ gurus in my department had a hard time with:

I'm writing unit tests for a project and need to call all member methods in a class. This one has an method with two different signatures:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class FrameConverter{
public:
   class Inputs {
      /* definition of Inputs class */
   }
   class Params {
      /* definition of Params class */
   }

  /* some code */

   inline Inputs& inputs(void) {
      return m_in;
   }

   inline const Inputs& inputs(void) const {
      return m_in;
   }

   inline const Params& params(void) const {
      return m_par;
   }

   /* some more code */

protected:
   Params m_par;
   Inputs m_in;
   /* some other stuff */
}


At first in my test program I tried to call the two different inputs() methods in this way:

1
2
FrameConverter::Inputs& rMyInput1 = myFrameConverter->inputs();
const FrameConverter::Inputs& rMyInput2 = myFrameConverter->inputs();


But it didn't work! Both calls were to the first inputs-method without the const's (We use BullseyeTM to check the code coverage). Now, in order to call the params() method I write

const FrameConverter::Params& rParams = pFrameConverter->params();

which works fine. After some trying I found out that I can invoke the the second method 'const inputs' by making the class FrameConverter constant. But then I cannot test it because I need to change internal data for that. After some more trying I ended up with the following solution:

const FrameConverter::Inputs& rMyInput = ((const MyFrameConverter*)myFrameConverter)->inputs();

Now, I've checked in my c++ book for the usage of const and found nothing about this behavior. The c++ experts I've talked to didn't know about it either.

The purpose of the two difernt signatures is also not clear to me; which happens a lot when you write tests for someone else's code. It might be that programer forgot to delete the first inputs-method.

Any ideas?

Last edited on
What's your point ??
This baffled someone who calls himself a "guru"? Obviously, the compiler is calling the overload that better matches the parameters being passed. Although IMO it should give an ambiguous call error. After all, a 'T' can be implicitly casted to a 'const T'.
Yes, my theory goes along the lines that the compiler realizes there is no point of using the function with the const return reference. But what is happening when I make the entire class const? Would then all the functions returning reference need to be const, in which case the non-const inputs() would never be called?

It is I who calls them gurus.

Thanx
my theory goes along the lines that the compiler realizes there is no point of using the function with the const return reference.
No, the compiler can't decide on an overload based on the return type, only based on the parameters passed. As a matter of fact, an overload of a function differing only in the return type is illegal.
The compiler sees that the this parameter is non-const, so it makes a smart guess that the function being called might need to modify it. If that's what you intended and the function being called was const, that would be a problem.

But what is happening when I make the entire class const?
Don't confuse the terms "class", "object" or "instance", and "pointer to object".
What's happening is that a function that takes a pointer to a variable object (f(T *)) is not the same as a function that takes a pointer to a constant object (f(const T *)). You can pass a 'T *' to either one, but a 'const T *' can only be passed to the latter.
This is what's happening here. const between the closing parentheses and the semicolon or opening brace of a member function means "this function can take a pointer to a constant object as the this parameter". Functions that aren't declared this way can only take pointers to variable objects.
When you cast the pointer to 'const T *', you're guaranteeing that the compiler will only be able to call the overload that can take that as a parameter. If that overload wasn't there, the compiler would complain either about an illegal implicit cast to 'T *', or about not having a matching overload for that parameter type.
Thanks hilios, I'm beginning to catch on (even the compiler's error messages make sence now). You're the guru!
This paradigm is very common:

1
2
3
4
5
class IntArray {
  public:
    int get( int index ) const;
    int& get( int index );
};


When given a const instance of IntArray, only the const version of get() can be called.
When given a non-const instance of IntArray, technically speaking, either could be
called, but the non-const version is a closer match, so it is called.

So:
1
2
3
4
5
6
7
8
// Assume array = { 1, 2 }
bool TestConstGet( IntArray& array )
{
    // By forcing the object to be const, we guarantee the const version of get() will be called.
    const IntArray& c_array = static_cast<const IntArray&>( array );
    // These call the const version
    return c_array.get( 0 ) == 1 && c_array.get( 1 ) == 2;
}


This behavior is guaranteed by the standard.
jsmith shouldn't the const version also return a reference?
Not necessarily.

You might consider returning a reference if it is expensive to return a copy. It is as cheap to return an int
as it is a reference to an int.
Topic archived. No new replies allowed.