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
- type-qualifier:
const
restrict
volatile
_Atomic
[...]
Semantics:
- 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
Why Do C and C++ Compilers Allow Array Lengths in Function Signatures When They'Re Never Enforced
Does the Size of an Int Depend on the Compiler And/Or Processor
Inline Functions VS Preprocessor Macros
Why Should the Copy Constructor Accept Its Parameter by Reference in C++
What Happens to Global and Static Variables in a Shared Library When It Is Dynamically Linked
What Is the Advantage of Using Forwarding References in Range-Based For Loops
Detecting Signed Overflow in C/C++
Linux API to List Running Processes
Returning a Reference to a Local or Temporary Variable
Are There Any Valid Use Cases to Use New and Delete, Raw Pointers or C-Style Arrays With Modern C++
Significance of Ios_Base::Sync_With_Stdio(False); Cin.Tie(Null);
Shared_Ptr to an Array: Should It Be Used
C++ Deprecated Conversion from String Constant to 'Char*'
Static Constant String (Class Member)
Difference Between 'New Operator' and 'Operator New'
How Is Meyers' Implementation of a Singleton Actually a Singleton