Would it be undefined behavior to change where a pointer points, when its data is const? Example:
const char* p = "foo";
p = "boo";
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
Extra question: and altering not const data of a const pointer? Would be UB? Example:
char* const p = "foo";
(*(char**)&p) = (char*)malloc(strlen(p));
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
This is correct. The pointer is not const so you can change it to point to something else if you want. In this case it won't cause a meory leak but remember that if the pointer points to data allocated with new and it is the only pointer to that data then you need to call delete before reassigning the pointer otherwise you'll have a meory leak.
Extra question: and removing the constness of pointer? Would be UB?
It is only UB if you try to modify a const object you removed const from, which you do in this case. Just removing const is okay, and sometimes needed, but you are never allowed to modify the object unless it was not const to begin with. For example the following is legal since foo is not const.
int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
on the other hand
const int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
is not legal as foo is const
The code in the first snippet is 100% correct. You have a pointer to const p, which you repoint to something else. All good and in mint condition.
The second piece of code is ill-formed. You can't modify an object after removing the constness, if original object was const-qualified (which string literal is).
Related
I've encountered similar code piece today and it got me wondering. I did a little experiment myself as shown below.
Why does the first stuff function with shared pointer allow to modify the value while seconds one doesn't?
#include <memory>
void stuff(const std::shared_ptr<int> &var)
{
*var = 5;
}
void stuff(const int* &var)
{
*var = 5;
}
int main()
{
auto a = std::make_shared<int>();
stuff(a);
int* b;
stuff(b);
return 0;
}
These two types:
const std::shared_ptr<int>
const int* (aka int const*)
are fundamentally different:
The first one is a const pointer to an int;
The second one is a pointer to a const int.
The shared_ptr equivalent of the version that doesn't permit modification of the pointee would be std::shared_ptr<const int>.
The raw pointer equivalent of the version that does permit modification of the pointee would be int* const.
Ability to modify the pointer, and ability to modify the pointed-to thing, are different things.
Would it be undefined behavior to change where a pointer points, when its data is const? Example:
const char* p = "foo";
p = "boo";
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
Extra question: and altering not const data of a const pointer? Would be UB? Example:
char* const p = "foo";
(*(char**)&p) = (char*)malloc(strlen(p));
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
This is correct. The pointer is not const so you can change it to point to something else if you want. In this case it won't cause a meory leak but remember that if the pointer points to data allocated with new and it is the only pointer to that data then you need to call delete before reassigning the pointer otherwise you'll have a meory leak.
Extra question: and removing the constness of pointer? Would be UB?
It is only UB if you try to modify a const object you removed const from, which you do in this case. Just removing const is okay, and sometimes needed, but you are never allowed to modify the object unless it was not const to begin with. For example the following is legal since foo is not const.
int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
on the other hand
const int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
is not legal as foo is const
The code in the first snippet is 100% correct. You have a pointer to const p, which you repoint to something else. All good and in mint condition.
The second piece of code is ill-formed. You can't modify an object after removing the constness, if original object was const-qualified (which string literal is).
I'm curious why the compiler doesn't complain if I hand over a const-pointer to a non-const-pointer as parameter in a constructor of a class that itself, which of course is at construction time not const.
#include <iostream>
class A
{
public:
A(int* v) : v(v) {}
int* v;
};
class B
{
public:
B() : o(23), v(&o) {}
const A cc() const
{
return A(v); //expected const to non-cosnt error here, but seems valid
}
int o;
int* v;
};
int main() {
B b;
const B* bc = &b;
A a = bc->cc();
*(a.v) = 25; // here I'm changing an originally const value
std::cout << b.o << std::endl;
return 0;
}
With defining cc() as const I expected the line where the return value is initialized an error message about converting a const to a non-const. The code-snippet compiles fine and in the end I get the output "25" even so that should have been const.
The A constructor expects a copy of a pointer instead of a reference. It is absolutely legal to copy a int * const into an int*. Therefore, no error occurs.
You should use references instead of pointers when you want a consistent behavior regarding constness. By the way, returning const objects is a bad idea.
You're confusing int const* (or const int*) and int* const.
Copying a const object into a non-const new object is fine, whether that object is a pointer or not. That's all you're doing here; the pointer itself is const (it's int* const), so you can pretty much do what you like with a copy of it.
What is not ok is copying a int const* into a int*, because now you're saying the thing being pointed to has magically lost its const-protection.
constness of an object does not propagate to constness of the pointee of a pointer member. The pointer becomes const but the pointee stays non-const. Even if the pointer is const, you can easily obtain a non-const pointer by simply copying the pointer which is exactly what happens as the pointer is passed by value.
I would pass T*& pointer, when I am intending to change the pointed value inside the function:
void foo(char *&p)
{
p = (b == true)? new char[10] : 0;
}
But I am not able to get what is the use case for T* const& kind of pointer (since that pointer is not changeable)? I mean why should not I pass simply T* const ?
void foo(char* const &p); // p is not changeable
void foo(char* const p); // p is not changeable
You would use a T* const & as a parameter if the value of the pointer object might be changed by something external to your function and you wanted to be able to observe changes to the value of the pointer object or if you wanted to store a reference or pointer to the pointer object for later reading.
A T* parameter (equivalent to T* const as a function parameter) just gives you a copy of the pointer object, a snapshot of its value when it was passed to your function.
void foo( char* const& ptr )
{
char* p1 = ptr; // initial value
global_fn(); // ptr might be changed
char* p2 = ptr; // new value of ptr
}
vs
void foo2( char* ptr )
{
char* p1 = ptr; // initial value
global_fn(); // ptr can't be changed, it's local to this function
char* p2 = ptr; // will be the same as p1
}
Technically, even the function itself might change the value of the pointer to which it is passed a reference.
E.g.
char* p;
std::ptrdiff_t foo( char* const& ptr )
{
++p;
return p - ptr; // returns 0, would return 1 if the parameter was by value
}
int main()
{
char test[] = "Hello, world!";
p = test;
foo( p );
}
The difference is realistically nil. const references are used to prevent copying of expensive-to-copy or, in generic code, uncopyable types, but since pointers are trivial, it's negligible and you may as well take by value.
I think a simpler example would illustrate the point that Charles Bailey is making. Let's remove the issue of the pointer part of it, because for this question it is irrelevant. So your question basically becomes:
void foo(const int &p); // p is not changeable
void foo(const int p); // p is not changeable
Do you see more clearly how it works? Yes, the local variable "p" cannot be assigned to in both cases. And yes, neither piece of code will affect the variable in the calling scope. But in the former example p could be a reference to a variable (non-const) int that can be changed while the latter is an argument passed by value which could not be changed. (Actually the const in the second example has no effect on anything outside the function, so it is kind of superfluous. Same with the second example in the question.)
In C++ a stack-allocated object can be declared const:
const Class object;
after that trying to call a non-const method on such object is undefined behaviour:
const_cast<Class*>( &object )->NonConstMethod(); //UB
Can a heap-allocated object be const with the same consequences? I mean is it possible that the following:
const Class* object = new Class();
const_cast<Class*>( object )->NonConstMethod(); // can this be UB?
is also undefined behaviour?
Yes. It's legal to construct and destroy a const heap object. As with other const objects, the results of manipulating it as a non-const object (e.g. through a const_cast of a pointer or reference) causes undefined behaviour.
struct C
{
C();
~C();
};
int main()
{
const C* const p = new const C;
C* const q = const_cast<C*>(p); // OK, but writes through q cause UB
// ...
delete p; // valid, it doesn't matter that p and *p are const
return 0;
}
In your heap example, new returns a pointer to non-const. The fact that you've stored it in a pointer to const (and then const_casted it back to a pointer to non-const) doesn't change the fact that the object itself is not const in the same way as the stack-allocated one is.
However, you can create a const object on the heap:
const Class* object = new const Class();
In such a case, casting to a pointer to non-const and calling a non-const method would be the same situation as the const stack-allocated object.
(The idea of creating a const object on the heap was new to me, I had never seen that before. Thanks to Charles Bailey.)
Yes, a heap-allocated object can be const. Consider this excerpt from the example in 7.1.5.1/5:
const int* ciq = new const int (3); // initialized as required
int* iq = const_cast<int*>(ciq); // cast required
*iq = 4; // undefined: modifies a const object
The example you gave in the question is fine because you're not asking new to make a const object; you're just storing the result in a pointer-to-const.
Don't forget mutable members
It won't be undefinied behaviour if the NonConstMethod only modifies mutable qualified members (see 7.1.5.1 (4)) of a const qualified class. Yes, otherwise it's undefined behaviour.
const A* p = new(const A);
A *q = const_cast<A*>(p);
q->NonConstMethodThatModifiesMembers(); // undefined behaviour!
q->NonConstMethodThatOnlyModifiesMutableMembers(); // defined behaviour!
Obviously:
struct Foo {
const int Bar;
Foo() : Bar(42) { }
};
Foo* foo = new Foo;
const_cast<int&>(foo->Bar); // don't do this.
const_cast can cause UB when the object is actually read-only (for example, the compiler can create such objects when you use hard coded strings in your code, by placing them in certain memory areas that are read only) for some reason. This will not happen with heap allocated objects, no matter how you keep their reference (const pointer, const reference, whatever).