The PDF article you linked contains a number of incorrect statements about differences between C and C++ in their treatment of top-level cv-qualifiers. These differences either do not exist or have different nature from what is implied in the article.
In reality both C and C++ effectively ignore top-level cv-qualifiers in function parameter declarations when it comes to determining function signature and function type. The wording in C and C++ language standards (and the underlying mechanisms) can be conceptually different, but the end result is the same in both languages.
C++ does indeed directly ignore top-level cv-qualifiers on parameters when determining function type, as described in 8.3.5/5: "After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type."
Instead of directly ignoring such qualifiers, C relies on the C-specific notion of compatible type. It says that function types that differ only in top level cv-qualifiers on parameters are compatible, which for all means and purposes means that they are the same. The definition of the function type compatibility in 6.7.5.3/15 says: "In the determination of type compatibility and of a composite type, [...] each parameter declared with qualified type is taken as having the unqualified version of its declared type."
The linked PDF article states that in C the following sequence of declarations is illegal
void foo(int i);
void foo(const int i);
In reality it is perfectly legal in C. C simply requires that all declarations of the same entity in the same scope use compatible types (6.7/4). The two declarations above are compatible, which means that they simply legally redeclare the same function. (In C++ the above declarations are also legal and they also redeclare the same function.)
For additional examples, in both C and C++ the following initializations are valid
void foo(const int i);
void bar(int i);
void (*pfoo)(int) = foo; // OK
void (*pbar)(const int) = bar; // OK
At the same time both C and C++ identically take into account top-level cv-qualifiers when it comes to determining local variable type of function parameter. For example, in both C and C++ the following code is ill-formed
void foo(const int i) {
i = 5; // ERROR!
}
In both C and C++ a function declared with one top-level cv-qualification on its parameters can be later defined with a completely different cv-qualification of its parameters. Any differences in top-level cv-qualification do not constitute function overloading in C++.
Also, you repeatedly mentioned that char *[]
is interpreted as char **
as something relevant. I don't see the relevance. In function parameter lists T []
declarations are always equivalent to T *
declarations. But this has absolutely nothing to do with top-level cv-qualifiers.
Meanwhile the code sample in your edit fails to compile for reason that have nothing to do with top-level cv-qualifiers either. It fails to compile because there's no implicit conversion from char **
to const char *const *
in C language. Note that this conversion does not involve and does not care about any top-level cv-qualifiers at all. The const
qualifiers that affect this conversion are present only on the first and second levels of indirection.
This does indeed involve a difference between C and C++. In both C and C++ implicit conversion from char **
to const char **
is not allowed (see here for example). However, C++ allows implicit conversion from char **
to const char *const *
while C still doesn't. You can read more about it here. But note, again, that in all these cases top-level cv-qualifiers are completely irrelevant. They play no role at all.