Date: Tue, 1 Jul 2025 17:26:02 +0200
I think there was an (unavoidable) missed opportunity in C++98 when
new[] and delete[] were introduced.
What we have currently is unchanged since 1998 and very C:
new std::string[8] returns an object of type std::string*. It is
indistinguishable in its type from the result of new std::string.
When you free it, you must remember that it was allocated via new[] and
pass it into delete[] instead of delete.
Calling delete instead of delete[] is undefined behavior and may lead to
crashes in practice not just memory leaks.
Instead, we could have the following:
new T[n] returns an object of type T[],
we can declare variables of type T[], they have a representation
identical to T*
objects of type T[] decay to T* similar to array decay,
delete p has the behavior of delete[] when p is of type T[].
This would represent the difference in the type system and not just in
the logic within functions.
I think the state of T[] is very odd in the current language:
variables cannot be declared:
int p[]; // will not compile
struct members can be declared, but this is C (flexible array members)
and not allowed in strict C++
struct S {
int p[];
};
There are headers written in C that use use this syntax, and these won't
change to not break existing code.
Function parameters can be declared, but is no different from declaring
a pointer
void fun(int p[]); is the same as void fun(int* p);
As far as I can tell, T[] in C++ is mostly used in specializations of
templates like std::unique_ptr<T[]> which is essentially syntactic sugar
over std::unique_ptr<T,array_deleter<T>>, as an array without bound T[]
cannot be meaningfully used in the current language.
I think the C++ language rules can be amended to allow T[] to represent
T* allocated by new[] and backwards compatibility with C headers can
preserved by disallowing this construct within extern "C" constructs.
Of course, it is easy to imagine generic C++ code that breaks when the
expression new[] returns a type that decays to T* instead of T*.
Whether that is relevant in practice can only be determined by
implementing the proposed changes in a compiler.
I just thought I ask whether I am the only one who thinks this might be
a good idea before (asking for help) implementing this in a branch of
GCC or LLVM.
Regards,
Henning
new[] and delete[] were introduced.
What we have currently is unchanged since 1998 and very C:
new std::string[8] returns an object of type std::string*. It is
indistinguishable in its type from the result of new std::string.
When you free it, you must remember that it was allocated via new[] and
pass it into delete[] instead of delete.
Calling delete instead of delete[] is undefined behavior and may lead to
crashes in practice not just memory leaks.
Instead, we could have the following:
new T[n] returns an object of type T[],
we can declare variables of type T[], they have a representation
identical to T*
objects of type T[] decay to T* similar to array decay,
delete p has the behavior of delete[] when p is of type T[].
This would represent the difference in the type system and not just in
the logic within functions.
I think the state of T[] is very odd in the current language:
variables cannot be declared:
int p[]; // will not compile
struct members can be declared, but this is C (flexible array members)
and not allowed in strict C++
struct S {
int p[];
};
There are headers written in C that use use this syntax, and these won't
change to not break existing code.
Function parameters can be declared, but is no different from declaring
a pointer
void fun(int p[]); is the same as void fun(int* p);
As far as I can tell, T[] in C++ is mostly used in specializations of
templates like std::unique_ptr<T[]> which is essentially syntactic sugar
over std::unique_ptr<T,array_deleter<T>>, as an array without bound T[]
cannot be meaningfully used in the current language.
I think the C++ language rules can be amended to allow T[] to represent
T* allocated by new[] and backwards compatibility with C headers can
preserved by disallowing this construct within extern "C" constructs.
Of course, it is easy to imagine generic C++ code that breaks when the
expression new[] returns a type that decays to T* instead of T*.
Whether that is relevant in practice can only be determined by
implementing the proposed changes in a compiler.
I just thought I ask whether I am the only one who thinks this might be
a good idea before (asking for help) implementing this in a branch of
GCC or LLVM.
Regards,
Henning
Received on 2025-07-01 15:26:06