Deleting a pointer to const (T const*)
It's to support:
// dynamically create object that cannot be changed
const Foo * f = new Foo;
// use const member functions here
// delete it
delete f;
But note that the problem is not limited to dynamically created objects:
{
const Foo f;
// use it
} // destructor called here
If destructors could not be called on const objects we could not use const objects at all.
How does delete deal with pointer constness?
const_cast doesn't really do anything – it's a way to suppress compiler moaning about const-ness of the object. delete keyword is a compiler construct, the compiler knows what to do in this case and doesn't care about const-ness of the pointer.
Was the behavior of deleting pointers-to-const changed in the past?
You can indeed delete
a pointer-to-const
.
If Visual C++ says otherwise for a standard-conforming program, then that's a compiler bug and should be reported.
Your (or Microsoft's?) program is not standard C++, however, since you have void
result type for main
.
The KB article you link to says "Deleting a pointer to a constant should not be allowed by definition (ARM section 5.3.4)", and although it's wrong, the reference it gives is correct. The ARM section 5.3.4 says "A pointer to constant cannot be deleted". However, the ARM was published in 1990...
C++ was standardized about ten years later, in 1998, and in standard C++ you can delete a pointer to const
. This is not specified in the normative text; it's specified by omitting the restriction. However, the C++98 standard §5.3.5/2 has the following non-normative note:
a pointer to a
const
type can be the operand of a delete-expression; it is not necessary to cast away the constness (5.2.11) of the pointer expression before it is used as the operand of the delete-expression.
We are now over ten years after that standardization again, over 20 years after the ARM.
Which version of Visual C++ are you using?
Cheers & hth.,
Why can delete operator be used in const context?
So I think
delete p;
may change the memberp
ofrhs
thoughrhs
is a const reference.
No.
delete p;
doesn't changep
. Invalidation is not modification.Regardless, having a const reference to an object (
rhs
) does not by any means prevent the referred object form being modified. It merely prevents modification through the const reference. In this case we access the object throughthis
which happens to be a pointer to non-const, so modification is allowed.
Someone may insist that "to render a pointer invalid is not to change the value of a pointer" but I don't find such a statement in the standard.
The behaviour of delete expression is specified in [expr.delete]. Nowhere in that section does it mention that the operand is modified.
Becoming invalid is specified like this:
[basic.compound]
... A pointer value becomes invalid when the storage it denotes reaches the end of its storage duration ...
Note that it is the value that becomes invalid. The pointer still has the same value because the pointer was not modified. The value that the pointer had and still has is simply a value that no longer points to an object - it is invalid.
Supplement: I've changed
delete p;
todelete rhs.p;
, but it still works. Why?
Answer 2. From previous question no longer applies, but answer 1. does. delete rhs.p;
does not modify rhs.p
.
Why delete can perform on pointers to const while free cannot?
free
should not be used with actual C++ objects. free
should be used with malloc
, so you shouldn't be using free
on something you allocated with new
.
As to why you can delete
const objects, that's simple:
const Type *ptr = new Type(...);
Now what? If you couldn't delete that, then you'd have to do this:
delete const_cast<Type*>(ptr);
Having an object be const
means that it cannot be modified. You cannot cause the object to go from one state to another state. Deletion is throwing it away. Meaning that it no longer exists in any state, whether the original one or some other modified form. Deletion is an operation that exists outside of the object's mutability state, much like construction.
Conceptually, deletion is neither const nor non-const. While a destructor is a non-const function, the idea of destructing an object is simply outside the domain of const or non-const.
OK, let's say you define operator delete
to take a pointer to const (which is different from a const pointer):
void operator delete(void const* p);
What is the first line of this function going to be? The purpose of operator delete
is to free memory that was allocated by operator new
. That will require poking at bits on the memory allocation heap. And to do that, you need a pointer that does not point to const data:
void *ptr = const_cast<void*>(p);
Welcome to undefined behavior. While C++ allows you to do this, the specification is very clear that the results of attempting to write to ptr
(or any address based on it) are undefined. You were given a const pointer; you were told by the outside world not to modify the stuff it pointed to. C++ provides no guarantees about what happens when you break that contract.
Since the specification states that this is undefined behavior, and since operator delete
(in most cases) cannot do its job without modifying the memory pointed to by p
(or modifying memory based on that address), it would be silly of the specification to then allow you to define operator delete
this way. It would basically be canonizing the idea of shooting yourself in the foot.
Yes, in virtually every case, this would be completely safe. But since you're going to cast away the const anyway, why even bother to allow the rather dubious idea in the first place?
Is it a good practice to free memory via a pointer-to-const
Well, here's some relevant stuff possibly too long to fit into a comment:
Some time ago the practice to free memory via a pointer-to-const was plain forbidden, see this dr. Dobb's article, the "Language Law" ( :)) part.
There has twice been a relevant discussion on http://groups.google.ru/group/comp.lang.c++.moderated: "Delete a const pointer?", "Why can operator delete be called on a const pointer" (both actually deal with the case in question, i.e. pointer to const).
My own point (since you are asking for arguments): possibility of the operation in question in any given context is defined by the (explicitly or implicitly defined in the documentation) contract of a class or a function, not by just the method signature or parameter types.
Why a const T* is trivially converted to void* in operator delete ?
While I quite agree with @JamesKanze's answer, perhaps somebody would like to see what the standard actually says. According to the standard (§12.1/4):
const and volatile semantics (7.1.5.1) are not applied on an object under
construction. Such semantics only come into effect once the constructor for the most derived object (1.8)
ends.
and (§12.4/2):
const and
volatile semantics (7.1.5.1) are not applied on an object under destruction. Such semantics stop being
into effect once the destructor for the most derived object (1.8) starts.
In fairness, this does little more than re-state what @James said, a bit more specifically: the object is only really considered an object from the time the ctor finishes (or all the ctors, when inheritance is involved) to the point that the first dtor begins. Outside those boundaries, const and volatile aren't enforced.
remove const from a typedef datatype
You can define a template function that is overloaded for pointers:
template <typename T>
T& remove_const(const T& value)
{
return const_cast<T&>(value);
}
template <typename T>
T* remove_const(const T* value)
{
return const_cast<T*>(value);
}
And call it as so:
std::array<const uint8_t, 8> buf = {'\0'};
BufType cdata = buf.data();
uint8_t* data = remove_const(data);
If you want a way to express the resulting type, i.e. remove the const
of the pointed-to type, you could define a type trait like this (or define template specializations, as in the function case):
template <typename T>
struct remove_pointed_to_const
{
using type = std::add_pointer_t<std::remove_const_t<std::remove_pointer_t<T>>>;
};
// This static_assert is correct.
static_assert(std::is_same<remove_pointed_to_const<BufType>::type, uint8_t*>::value);
In this alternative, you could use const_cast
directly at the point of use, which might make it easier to identify all const_cast locations in a project:
std::array<const uint8_t, 8> buf = {'\0'};
BufType const cdata = buf.data();
uint8_t* data = const_cast<remove_pointed_to_const<BufType>::type>(data);
As a comment said, and as with all const_cast
usages, you need to be careful not to apply this to an object that was initialized as a const object. Doing so would be UB.
Related Topics
Why Are Two Different Concepts Both Called "Heap"
How to Concatenate Multiple C++ Strings on One Line
Static Variables in Member Functions
What Are Some Reasons a Release Build Would Run Differently Than a Debug Build
Why Do Std::Shared_Ptr≪Void≫ Work
How to Convert String to Char Array in C++
How to Specify How Many Characters of a String to Print Out Using Printf()
How to Read and Manipulate CSV File Data in C++
How to Get Std::Vector Pointer to the Raw Data
Why Does Typeid.Name() Return Weird Characters Using Gcc and How to Make It Print Unmangled Names
C++, Variable Declaration in 'If' Expression
What's Your Favorite Profiling Tool (For C++)
Vector of Vectors to Create Matrix
How to Automatically Convert Strongly Typed Enum into Int
In C/C++ What's the Simplest Way to Reverse the Order of Bits in a Byte