Non-const reference to non-const pointer to const char - c++

Why can't I take a reference to s2 in foo? I'm compiling with gcc 5.1.0:
#include <cstring>
void foo(const char*& p)
{
while (*p && *p <= '5')
++p;
}
int main()
{
const char *s1 = "1234567890";
char *s2 = new char[strlen(s1) + 1];
strcpy(s2, s1);
foo(s1); // OK
foo(s2); // error
return 0;
}
I compiled with:
$ g++ test_reference.cpp
The compiler gave me:
test_reference.cpp: In function ‘int main()’:
test_reference.cpp:16:11: error: invalid initialization of non-const reference of type ‘const char*&’ from an rvalue of type ‘const char*’
foo(s2); // error
^
test_reference.cpp:3:6: note: initializing argument 1 of ‘void foo(const char*&)’
void foo(const char*& p)
^

For simplicity, you are trying to do:
char* s = ...;
const char*& r = s;
The const here may be misleading. You would think this is equivalent to:
int i = 4;
const int& ri = i;
Which works. But these are not equivalent. ri here is a reference to a const type, but r is a reference to a pointer to const char, that is not a const type.
The ultimate issue is that char* and char const* are not reference-related (meaning that the types are either the same or in the same hierarchy). However, if your reference was a reference to a const type, it would work fine. That is:
const char* const& cr = s;
Basically, you can take an lvalue reference to a non-const type T only from a reference-related type or from a class that is convertible to a reference related type. But you can take an lvalue reference to a const type from a much wider source of expressions:
double d = 4.0;
int& ri = d; // error: int, double aren't reference-related
const int& rci = d; // OK

You can cast it to const reference.
foo((const char *&) s2);

Related

C++) why const int*& parameter can't take int* argument? [duplicate]

This question already has answers here:
Binding a const pointer reference to a non-const pointer
(2 answers)
Closed 11 months ago.
before writing, I'm not good at english. So maybe there are many awkward sentence.
void Func1(const int* _i) { };
void Func2(const int& _j) { };
void Func3(const int* (&_k)) { };
int main()
{
int iNum = 1;
int* pInt = new int(1);
Func1(pInt); // works;
Func2(iNum); //works
Func3(pInt); // error
}
I use Visual Studio and error message said
"Cannot convert argument 1 from 'int *' to 'const int *&'"
I know it cant convert because of '&'. _i equals pInt so it may change dereference.
But i used const. so i think it will work but const keyword doesnt work.
why const keyword doesnt work unlike other cases? ex)Func1,Func2
Func1(pInt); // works; int* could convert to const int* implicitly
Func2(iNum); //works; int could be bound to const int&
Func3(pInt); // error;
pInt is a int*, when being passed to Func3 which expects a reference to const int*, then it would be converted to const int*, which is a temporary and can't be bound to lvalue-reference to non-const like const int* & (lvalue-reference to non-const pointer to const int).
If you change the parameter type of Func3 to int* &, then no conversion is required, pInt could be bound directly. Or change to const int* const & (lvalue-reference to const pointer to const int) or const int* && (rvalue-reference) which could bind to temporary.

What types of objects can constexpr pointers point to?

cppreference.com says:
A constexpr specifier used in an object declaration implies const.
But I tried to make a constexpr pointer hold the address of a const object of the same base-type but the compiler gave me an error:
const int a = 1;
int main(){
constexpr int *b = &a;
return 0;
}
So, what types can a constexpr pointer point to?
The issue here is not with constexpr. If you said
int *b = &a;
You'd get the same error. i.e. "invalid conversion from const int* to int*"
We can fix that by making it a pointer to a const int.
int const *b = &a;
Now we can add constexpr, and yes, constexpr does imply const
constexpr int const *b = &a;
where b is in fact const. This is exactly the same as the following
constexpr int const * const b = &a;
//^^^^^
// this const is made redundant by the constexpr.
Your example doesn't compile because 'a' is a 'const int', and requires a 'constexpr const int' pointer to point to it.

make 'const' work with the result of type traits

int main(int argc, char* argv[]) {
const int i = 10;
using Type = typename std::conditional<false, int, int&>::type;
const Type r = i; // It seems this 'const' does not have any effect.
std::cout << r << std::endl;
}
The above code cannot compile on gcc4.8.1 -std=c++11, and the error message goes as following: "invalid initialization of reference of type 'Type {aka int&}' from expression of type 'const int'. But, it will work if I change the code like this:
int main(int argc, char* argv[]) {
const int i = 10;
using Type = typename std::conditional<false, const int, const int&>::type;
Type r = i;
std::cout << r << std::endl;
}
Is there any body can show me the reason?
In the first example, the const has no effect because it binds to the wrong type: after the reference rather than before. See https://www.securecoding.cert.org/confluence/display/cplusplus/DCL33-CPP.+Never+qualify+a+variable+of+reference+type+with+const+or+volatile for some details that may help, but basically:
const int& // reference to immutable int
int const& // reference to immutable int
int& const // immutable reference to int
Of course, all references are immutable (cannot be reseated), so the last one, which you have, is a bit useless. It happens because you apply the const after the reference has already been applied to the type, which gives you the third example I wrote rather than the first or second.
You can also change
const int i = 10;
to
int i = 10;
to make it work.
when you say
using Type = typename std::conditional<false, int, int&>::type;
it is equivalent to:
typedef int& Type;
When you say
const Type r = i;
What you are saying is
int& const r = i;
That's not the same thing as
const int& r = i;
The difference between those was explained by John Zwinck in his answer.

Why can't a const T*& bind to a T*?

VS2010 shows error C2440: 'return' : cannot convert from 'char *' to 'const char *& in f3 below. Why is it not possible to return a char* when the return type is const char*&?
const char* const& f1()
{
static char* x = new char[5]();
return x;
}
char* const& f2()
{
static char* x = new char[5]();
return x;
}
const char* & f3()
{
static char* x = new char[5]();
return x; // error C2440
}
char* & f4()
{
static char* x = new char[5]();
return x;
}
From Paragraph 8.5.3/2 of the C++11 Standard:
A reference cannot be changed to refer to another object after initialization. Note that initialization of a
reference is treated very differently from assignment to it. Argument passing (5.2.2) and function value
return (6.6.3) are initializations.
This basically tells you that returning a value from a function is equivalent to performing an initialization. Therefore, function f3() doesn't compile for the same reason the last initialization in the code snippet below doesn't compile:
char c = 'a';
char* x = &c;
const char*& y = x; // ERROR!
The type of the object referenced by y is const char*, while the type of the expression we are trying to initialize it with (i.e. the type of x) is char*. Those are different types, and when binding a reference the types of the initializer expression and of the object referenced by the initialized reference must be identical (with an exception for base and derived classes, which are not involved here), apart from top-level cv qualifications.
Here, the const qualification in const char* is not a top-level qualification, because it applies to the pointed object, and not to the pointer itself: while the pointed char value cannot be modified through a const char* pointer, the pointer itself can be reassigned.
In Standard terms, this means that the types const char* and char* are not reference-related:
Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is reference-related to “cv2 T2” if T1 is the same type as T2, or T1 is a base class of T2. “cv1 T1” is reference-compatible with “cv2 T2” if T1 is reference-related to T2 and cv1 is the same cv-qualification as, or greater cv-qualification than, cv2. [...]
On the other hand, function f2() compiles, consistently with the fact that the initialization in the last line below is legal:
char c = 'a';
char* x = &c;
char* const& y = x; // OK
Here, the type of the referenced object is char* const, which (unlike const char*) is reference-compatible with char* in the sense defined by the Paragraph quoted above *(the const qualification is a top-level qualification in this case).
In Particular, per 8.5.3/5:
A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:
— If the reference is an lvalue reference and the initializer expression
is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or
[...]
The omitted part is not relevant to this case. Here, char* const and char* are reference-compatible, and the initialization is legal (and f2() compiles). On the other hand, const char* and char* are not reference-compatible, and the initialization is illegal (and f3() does not compile).
For the same reason you can't cast from char ** to const char **. Otherwise you could write
f3() = "hello";
and a subsequent call to f3 would be able to write to the memory of the string literal, aliased to the static local x.
#include <iostream>
void problem( const char*& output, const char* input )
{
output = input; // legal, const char*& = const char*
}
int main() {
const char buff[] = "hello";
char* out = nullptr;
problem( const_cast<const char*&>(out), &buff[0] );
out[0] = 'H'; // I just changed a const array's first character
std::cout << &buff[0] << "\n";
}
This prints "Hello", which is evidence I just engaged in undefined behavior. To do so, I had to use a const_cast. If you could initialize a const char*& with a char*, I wouldn't have to do the const_cast to invoke the undefined behavior.
"But" one might say "this doesn't match the exact case that the OP posted!"
Which is true.
// the "legal" way to do what the OP wants to do, but it leads to undefined
// behavior:
const char*& problem2( char*& c ) { return const_cast<const char*&>(c); }
void problem3( const char*& left, const char* right ) { left = right; }
#include <iostream>
int main() {
char* c = nullptr;
char const* buff = "hello undefined behavior";
problem3( problem2(c), buff );
c[0] = 'H';
std::cout << buff << "\n";
}
also prints "Hello undefined behavior".
"But", one might say, "you turned a char*& into a const char*&, not a char* into a const char*&.
Very well:
void problem3( const char*& left, const char* right ) { left = right; }
const char*& problem4( char x = 0 ) {
static char* bob = nullptr;
if (bob && x)
bob[0] = x;
return const_cast<const char*&>(bob);
}
#include <iostream>
int main() {
char const* buff = "hello problem";
problem3( problem4(), buff );
problem4('H');
std::cout << buff << "\n";
}
Again, that incriminating capital H (well, actually, undefined behavior that typically is a capital H).

const parameter problems

#include <iostream>
void f(const int * & p)
{
int i =0;
i = p[0];
std::cout << i << std::endl;
}
int main()
{
int * p =new int[1];
p[0] =102;
f(p);
return 1;
}
The gcc compiler gives error for this code:
prog.cpp: In function ‘int main()’:
prog.cpp:16: error: invalid initialization of reference of type ‘const int*&’ from expression of type ‘int*’
prog.cpp:5: error: in passing argument 1 of ‘void f(const int*&)’
But if I change the "f" function into
void f(const int * const & p)
Everything is ok. Can somebody explain why const behaves this way? thanks.
Going from int* to const int* requires creating a temporary const int* pointer and binding the reference const int*& to that temporary.
The Standard forbids doing that creating of a temporary for non-const references. You therefor need to make the reference const as you did your fix.
This is because non-const references mean "I want to change the argument that the caller passes using that reference parameter". But if the caller needs to convert their argument and ends up passing a temporary, the point of the reference is for naught, and so the Standard deems it an error to try and pass the temporary.
If the first conversion (int * to const int * &) were allowed, then you could write an evil function like this:
const int really_const[] = {1,2,3};
void evil(const int * & p)
{
p = really_const;
}
int main()
{
int not_const[3];
int * p = not_const;
evil(p);
p[0] = 0; // Whoops: modifying a const object
}
The second conversion is fine, since it prevents the function from modifying the pointer in this way.