Why Isn't It Legal to Convert "Pointer to Pointer to Non-Const" to a "Pointer to Pointer to Const"

Why isn't it legal to convert pointer to pointer to non-const to a pointer to pointer to const

From the standard:

const char c = 'c';
char* pc;
const char** pcc = &pc; // not allowed
*pcc = &c;
*pc = 'C'; // would allow to modify a const object

Why is an implicit conversion from non-const to const allowed for pointers in the first place?

From the C11 standard (draft N1570):

6.7.3 Type qualifiers


Syntax

  1. type-qualifier:

    const

    restrict

    volatile

    _Atomic

[...]

Semantics:


  1. The properties associated with qualified types are meaningful only for expressions that are lvalues.

[...]

EXAMPLE 1

An object declared

extern const volatile int real_time_clock;

may be modifiable by hardware, but cannot be assigned to, incremented, or decremented.

In simple terms:

const doesn't mean a value never changes. It only means that you are not allowed to change it1.

For callees , const is a restriction, not a promise.

For callers however, it is a promise. Passing a const pointer to a function, you can safely assume that the function will not change your data2, thus is "making a promise to you".


1 ...through the identifier with the const qualifier.

2 ...through the const argument passed to it.

Copy pointer to const to pointer to non const via memcpy

The initialization void *b = a; isn't valid C, it violates the rule of simple assignment C17 6.5.16.1 (initialization follows the rules of assignment), which states that in order for the expression to be valid:

...the type pointed to by the left has all the qualifiers of the type pointed to by the right.

You might want to compile with -pedantic-errors to get errors instead of warnings for C language violations.


As for well-defined behavior - just as long as you de-reference the pointer using the correct type of the actual data, it is well-defined behavior, and the type of the pointer itself doesn't matter much.

I don't even understand why you need to convert to void*, since the format of your callback is this:

int (*comp)(const void *, const void *)

So the only problem is the return type of the outer function, which could be simplified to something like this:

void* vector_lsearch (const void* key, const void* base, int (*comp)(const void*, const void*))
{
const struct vector* vector = CONST_VECTOR(base);
void* result = NULL;
unsigned char* data = (unsigned char*)base;

for (size_t i=0; i < vector->size; i++)
{
if (comp(&data[i*vector->szof], key) == 0)
{
result = data;
break;
}
}
return result;
}

CONST_VECTOR is fishy though, smells like you are hiding a cast behind a macro or something?

Why is it not allowed to assign const char * to const variable?

Because name is non-const, it implies you are allowed to change the values.

For example:

*name = 'S'; // Change from "something" to "Something"

But _name was declared const, meaning you cannot change it.

You cannot take fixed, constant data, and assign it to a different variable; that is saying "It's OK if you change this".

Non-const reference to a non-const pointer pointing to the const object

I cannot quite understand this error, as for me there is no rvalue involved (I am passing a reference to object, that already exists on the stack.)

int* and const int* are different things. When you pass a of type int* to function(const int*&), it need to be implicitly casted to const int* firstly, which is temporary, i.e. rvalue, and couldn't be bound to non-const referece. That's why compiler complains.

Question is, how can I do it properly?

You could change the type of a or the parameter type of function() to make them match exactly (might be const int* if you won't change the value pointed by the pointer), to avoid the implicit conversion and temporary variable. Or as @TartanLlama suggested, return the new value of pointer from function().

const pointer assign to a pointer

There is difference between constant pointer and pointer to constant. Constant pointer is a pointer (a number - memory address) that cannot be changed - it always point to the same object given via initialization:

int * const const_pointer = &some_int_var; // will be always pointing to this var
const_pointer = &some_other_var; // illegal - cannot change the pointer
*const_pointer = 2; // legal, the pointer is a pointer to non-const

Pointer to constant is a pointer whose pointed value cannot be changed:

const int * pointer_to_const = &some_int_var; // doesn't have to be always pointing to this var
pointer = &some_other_var; // legal, it's not a constant pointer and we can change it
*pointer = 2; // illegal, pointed value cannot be changed

You can always assign constant to variable i.e. const pointer to non-const pointer (a). You can cast pointer to non-const to a pointer to const (b). But you cannot cast pointer to const to a pointer to non-const (c):

int * pointer;
int * const const_pointer = &var;
const int * pointer_to_const;

/* a */
pointer = const_pointer; // OK, no cast (same type)

/* b */
pointer_to_const = pointer; // OK, casting 'int*' to 'const int*'

/* c */
pointer = pointer_to_const; // Illegal, casting 'const int*' to 'int*'

[EDIT] Below, this is not standard c++. However, this is common.[/EDIT]

String literal

"Hello"

is converted to constant pointer to const (const char * const):

char *pointer = "Hello"; // Illegal, cannot cast 'const char*' to 'char*'
char * const const_pointer = "Hello"; // Illegal, cannot cast 'const char*' to 'char*'
const char * pointer_to_const = "Hello"; // OK, we can assign a constant to a variable of the same type (and the type is 'const char*')
"Hello" = pointer_to_const; // Illegal cannot re-assign a constant

In above examples the second is your case. You tried to initialize pointer-to-non-const with a pointer-to-const when passing string literal as argument of your function. No matter if these pointers are constants or not, it's matter what do they point to.

Summary:

1) If you cast a pointer of some type to a pointer of another type, you cannot cast pointer-to-const to pointer-to-non-const.

2) If you have constant pointer, the same rules applies as to other constants - you can assign a constant to a variable but you cannot assign a variable to a constant (except initializing it).

// EDIT

As GMan pointed out, the C++98 standard (§4.2/2) allows to implicitly cast string literals (which are constant char arrays) to a non-const char pointer. This is because of backward compatibility (in C language there are no constants).

Of course such a conversion can lead to mistakes and compilers will violate the rule and show an error. However, GCC in compatibility mode shows only a warning.

conversion from const pointer to pointer

Your addPoint() takes a non-const pointer:

void Polygon::addPoint(Point* p) {

But you're trying to pass it a const Point*, hence the error. The compiler doesn't know that you end up not modifying what p points to - so it could be a violation of const to let you do what you're trying to do.

For instance, if addPoint() did:

void Polygon::addPoint(Point* p) { p->setX(42); }

It would be clearly wrong to let you pass a const Point* in.

However, since you don't actually need p to point to a non-const Point you can just change the signature to reflex this:

void Polygon::addPoint(const Point* p) {

Const pointer pointing to non-const data

In this case, there is a type mismatch

No; there is no type mismatch in this case. It is a pointer to non-cost and you initialise it with a pointer to non-const.

Alternatively, if you insist on there being a "mismatch", then it is analogous to the following "mismatch":

const int b = 42;

Why is the second case valid

Simply put: The constness of the initialiser is irrelevant to whether it initialises a const object or not. Besides, the initialiser is a prvalue of a non-class type so const qualification doesn't even apply to it.



Related Topics



Leave a reply



Submit