Question

Here's the essence of the code:

class DM
{
public:
    int something;
    DM() : something(0) { }
};

typedef DM * dm_t;
typedef dm_t & dm_ref_t;
typedef const DM * dm_const_t;
typedef dm_const_t & dm_cref_t;

int getSomething( dm_cref_t dm ) // CALLING getSomething DOES NOT COMPILE
{
    return dm->something;
}

int getSomethingAgain( dm_const_t dm )
{
    return dm->something;
}

int getSomethingOnceMore( dm_ref_t dm )
{
    return dm->something;
}

int main()
{
    dm_t dm = new DM;
    getSomething( dm ); // COMPILER ERROR COMES FROM HERE
    getSomethingAgain( dm );
    getSomethingOnceMore( dm );
    return 0;
}

And here's the compiler error:

implicit_cast.cpp: In function ‘int main()’:
implicit_cast.cpp:31: error: invalid initialization of reference of type ‘const DM*&’ from expression of type ‘DM*’
implicit_cast.cpp:13: error: in passing argument 1 of ‘int getSomething(const DM*&)’

That is, I have a non-const pointer that I'd like to pass into a function that accept a const pointer by reference. All is fine and dandy if the function would accept a non-const pointer by reference or a const pointer by value, but const pointer by reference does not accept non-const pointer.

For safety reasons I'd like the function to take a const pointer; for some further development reasons I'd like it to be passed by reference. It should be completely safe to automatically cast from non-const to const. Why doesn't g++ performs the cast?

I'd like to proceed further with a function defined like getSomething. How can I legalize these calls without adding explicit casts every time I call it?

Was it helpful?

Solution 3

This is more like the C++ name "resolution". In your case, you have a pointer to a const DM, but you want a const pointer, so you must use typedef dm_const_t const & dm_cref_t (a const reference to a const pointer)

Giving more details:

When you declare const int * a, you are saying that a is a constant pointer to an integer. It is the same as int const * a.

If you want a reference to a type, you declare int & b, meaning that b is a reference to an integer. Thinking the same way, const int & c is the same as int const & c (const always qualify the variable, not the type).

So, if you want a const reference to a const pointer, using the second notation is clearer (and the only one possible if not using typedefs): int const * const & d, meaning d is a constant reference to a constant pointer to an integer. Using typedefs:

typedef const int * int_cptr;
typedef const int_cptr & int_cptrcref;

Proof: http://ideone.com/V48Kxe

OTHER TIPS

It's not doing it because, after expanding the typedefs, you have

int getSomething( DM const* & dm )
{
  return dm->something;
}
...
int main()
{
  DM* dm = new DM;
  getSomething( dm );
}

g++ is correctly NOT allowing this automatically, because you are asking for a non-const lvalue reference to a constant pointer. You currently have a non-constant pointer. It can't create a temporary, since the temporary can't bind to the non-const lvalue reference.

Now, if you were to take the pointer by constant reference, it would work, since it would be allowed to bind to the temporary. However, I would just pass the pointer directly, since there is no value in passing a pointer by constant reference.

The reason why this isn't allow is because if it were, so would this (defeating the purpose of the const system)

const int i = 5;
void foo( const int* & p)
{
   p = &i;
}

void bar()
{
   int * p; 
   foo(p); // Normally not legal
   *p = 6; // And this is why
}

Now, all that being said, any time I see a reference to a pointer my first question to the developer is why they needed to do that. Occasionally, it's because you need to have an output parameter, but it's not something I would expect to be widespread.

Look at [conv.qual] (4.4/4) in the C++11 standard:

A conversion can add cv-qualifiers at levels other than the first in multi-level pointers, subject to the following rules: ... [Note: if a program could assign a pointer of type T** to a pointer of type const T** (that is, if line #1 below were allowed), a program could inadvertently modify a const object (as it is done on line #2).

The example given is:

int main() {
    const char c = ’c’;
    char* pc;
    const char** pcc = &pc; // #1: not allowed
    *pcc = &c;
    *pc = ’C’; // #2: modifies a const object
}

This is explained for pointers to pointers, but the same applies for references to pointers for exactly the same reasons.

You cannot.

You are passing by pointer which can mean that after function execution there could be two different references to the same memory, one declared const, one declared non-const.

If the non-const reference is used to change the memory then the const reference suddenly has its underlying memory would suddenly have become changed, which would be incorrect behavior.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top