Function call with pointer to non-const and pointer to const arguments of same address
While it is true that the behavior is well-defined - it is not true that compilers can "optimize for const" in the sense that you mean.
That is, a compiler is not allowed assume that just because a parameter is a const T* ptr
, the memory pointed to by ptr
will not be changed through another pointer. The pointers don't even have to be equal. The const
is an obligation, not a guarantee - an obligation by you (= the function) not to make changes through that pointer.
In order to actually have that guarantee, you need to mark the pointer with the restrict
keyword. Thus, if you compile these two functions:
int foo(const int* x, int* y) {
int result = *x;
(*y)++;
return result + *x;
}
int bar(const int* x, int* restrict y) {
int result = *x;
(*y)++;
return result + *x;
}
the foo()
function must read twice from x
, while bar()
only needs to read it once:
foo:
mov eax, DWORD PTR [rdi]
add DWORD PTR [rsi], 1
add eax, DWORD PTR [rdi] # second read
ret
bar:
mov eax, DWORD PTR [rdi]
add DWORD PTR [rsi], 1
add eax, eax # no second read
ret
See this live on GodBolt.
restrict
is only a keyword in C (since C99); unfortunately, it has not been introduced into C++ so far (for the poor reason that more complicated to introduce it in C++). Many compilers do kinda-support it, however, as __restrict
.
Bottom line: The compiler must support your "esoteric" use case when compiling f()
, and will not have any problem with it.
See this post regarding use cases for restrict
.
This is well-defined (in C++, not sure in C any more), with and without the const
qualifier.
The first thing to look for is the strict aliasing rule1. If src
and dst
points to the same object:
- in C, they must be of compatible types;
char*
andchar const*
are not compatible. - in C++, they must be of similar types;
char*
andchar const*
are similar.
Regarding the const
qualifier, you might argue that since when dst == src
your function effectively modifies what src
points to, src
shouldn't be qualified as const
. This is not how const
works. Two cases need to be considered:
- When an object is defined to be
const
, as inchar const data[42];
, modifying it (directly or indirectly) leads to Undefined Behaviour. - When a reference or pointer to a
const
object is defined, as inchar const* pdata = data;
, one can modify the underlying object provided it has not been defined asconst
2 (see 1.). So the following is well-defined:
int main()
{
int result = 42;
int const* presult = &result;
*const_cast<int*>(presult) = 0;
return *presult; // 0
}
1) What is the strict aliasing rule?
2) Is const_cast
safe?
This is well-defined in C. Strict aliasing rules do not apply with the char
type, nor with two pointers of the same type.
I'm not sure what you mean by "optimize for const
". My compiler (GCC 8.3.0 x86-64) generates the exact same code for both cases. If you add the restrict
specifier to the pointers, then the code generated is slightly better, but that won't work for your case, the pointers being the same.
(C11 §6.5 7)
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
In this case (without restrict
), you will always get 121
as a result.