Why are non-placement `new` and `delete` built into the language and not just regular functions?
Asked Answered
L

3

37

Why were the non-placement new expression and the delete expression implemented as language built-in instead of regular functions?

If we have...

  • a way of requesting/giving back memory to the OS

  • a way of explicitly invoking a constructor (placement new)

  • a way of explicitly invoking a destructor (~T())

...why couldn't non-placement new and delete just be regular functions in the Standard Library? Example:

template <typename T, typename... Ts>
T* library_new(Ts&&... xs)
{
    auto* ptr = /* request enough memory for `T` from OS */;
    new (ptr) T(std::forward<Ts>(xs)...);
    return ptr;
}

template <typename T>
void library_delete(T* ptr)
{
    ptr->~T();
    /* reclaim memory for `T` from OS */
} 
Laurentian answered 7/7, 2017 at 11:40 Comment(24)
Because the non-placement new and delete are in the language since the beginning and the placement versions were added later?Selfpossessed
@Selfpossessed The OP's question is why were non-placement new and delete put into the language instead of the standard library in the first place.Doorsill
@ThomasRussell the OP's question is why the non-placement new is not provided as a function implemented using placement new.Selfpossessed
@Selfpossessed I think that is equally worthy of an answer as Curious's was.Bellis
BTW, it seems that there are multiple reasons here. I would also be interested in knowing if there is any particular interaction with the concept of object lifetime or something related to polymorphism that might require language support.Laurentian
@VittorioRomeo I don't think polymorphism has any relevance here. I think it was purely a convenient way to do what your library functions have done back in the day.Sweeny
How does library_delete find the correct operator delete to return the memory to?Emblements
Don't think this question is a duplicate. Although the titles are the same, that is asking a different question - why can you simply not split up the new into 2 lines and do the same thing manuallySweeny
@Curious: indeed. I am basically asking if there's any "magic" involved in new/delete or if it could have been implemented as a library feature.Laurentian
@VittorioRomeo I don't believe there is any magic, your library replacements work just fine, and I would use them myself.Sweeny
@DavidRodríguez-dribeas seems to disagree.Laurentian
@VittorioRomeo Let's wait for him to explain what he meant. I don't follow either, it will be simple enough to just use malloc and free where you had comments.Sweeny
I marked this as a duplicate because it has exactly the same answer as that other question: stackoverflow.com/a/2286070. The "library replacements" mentioned in this question are somewhat comical, given their anachronicity. When the C++ language was introduced, and the new/delete operators were created, there was absolutely no way to write what you have written here! You needed the "magic" to be provided by the compiler.Bendy
@CodyGray: While I understand that the lack of variadic templates might have been a good reason, I'm more interested in "magic" similar to what David Rodriguez was hinting to.Laurentian
I assume that he's referring to the requirement in the standard that operator delete be called according to the dynamic type of the object being deleted. I don't see how your library_delete function can accomplish that. If you try to do operator delete(ptr);, you'll just end up calling the operator delete that is in scope, which isn't necessarily the correct one.Bendy
@CodyGray: I see. I found this article which explains the issue very well. I find that a way more important reason than the lack of variadic templates for the existence of a language-level delete.Laurentian
@VittorioRomeo I find your link to be a more convincing answer than any of the current ones, care to self answer?Synecology
@CodyGray questions can be different and have the same answers.Amoreta
@VittorioRomeo Interesting article! +1. Although I don't agree that that is a reason why library versions don't exist. That is basically using a consequence of the existence of those keywords as a reason to their existence. If there were library versions of new and delete, there would have been other ways users could override functions and provide such behaviour if needed.Sweeny
@Amoreta Philosophically, I suppose that is true. But practically, that isn't how we define "duplicate" here. It means "has the same answer". The UI says something to the tune of, "An answer to your question can be found here…" I'm also not convinced that the substantive portion of this question is any different. The presentation of the two questions is a bit different, but arguably that's just something that needs to be sorted out by editing the other question, since it is a bit unclear.Bendy
The original new dates back to C with Classes, circa 1980. Templates aren't accepted until ten years later, in 1990. Placement new dates to Cfront 2.0, in 1989.Damiendamietta
@Damiendamietta Mind if I use the information you gave in my answer?Sweeny
@Sweeny Feel free.Damiendamietta
Yet another useless header to include for the most basic functionality which should just work because 99.975% of all programs use it (just like move and intptr_t, size_t...). Great plan. On the contrary, I'd ask why is the standards committee to darn concerned about breaking compatibility with versions that are utterly incompatible anyway, and why are they so darn reluctant to adding keywords for things that make sense as keywords. Note how wchar_t is a keyword which is total bollocks, but size_t is not.Selfexamination
S
26

If the user's goal was to create an object in some memory location, then new seemed like a natural approach since forwarding references, variadic templates and placement new were not a thing back in those days. As correctly pointed out by @T.C. templates were released in 1990 and placement new in 1989. Variadic templates on the other hand, became a part of C++ only in C++11.

tl;dr There was no way to forward a bunch of arguments to a constructor of an arbitrary type (as you can do these days with make functions).

Sweeny answered 7/7, 2017 at 11:50 Comment(3)
It is the last paragraph that is crucial and I think you should make it the first. "Historical accident: In the original version of C++ there was no way to write new as a library function. Once new is a keyword, delete seems to need to be too."Uncritical
@MartinBonner Thanks for the suggestion but I think it's fine as is. The last paragraph might not be clear to someone without the second one. They might ask - "Why is there no good way? There sure is, see make_tuple for instance"Sweeny
Variable templates... In order to return correct type (instead of void*) you'd need plain ol' templates, which we hadn't back then.Cortie
S
14

Maybe this is not the best reference but this is what Wikipedia says about placement new in C++:

In earlier versions of C++ there was no such thing as placement new; instead, developers used explicit assignment to this within constructors to achieve similar effect. This practice has been deprecated and abolished later, and third edition of The "C++ Programming Language" doesn't mention this technique. Support for placement new operator has been added to compilers circa 1995.

Maybe in 2017 it is possible to implement new as a standard library function. Your suggested implementation uses language features that were added recently (many of them after 2010).

The C++ language, however, is much older (since 1983) and in the beginning there were no variadic templates, no typename, no placement new, no forwarding references.

In the beginning there was only the regular new and it had to be a language feature at that time because there was no way to implement it as a library function.

Selfpossessed answered 7/7, 2017 at 11:56 Comment(2)
Yeah, the whole point of new was that it returned ready-to-use pointer of correct type, no need to cast from void*. Without templates it wasn't possible in any other way than keyword.Allantoid
Placement new is in Cfront 2.0, released in 1989.Damiendamietta
M
4

If they were already provided as standalone functions then it would be impossible to provide user-defined replacement for them.

e.g. right now according to standard it is legel to write my own globl new and delete they will be used thoughout the program.

18.6.2 Storage allocation and deallocation [new.delete]

2 Replaceable: A C ++ program may define functions with either of these function signatures, and thereby displace the default versions defined by the C ++ standard library.

If those were supplied just like the rest of library functions then every normal call to new or delete would result "more than one instance of overloaded function matches arguments" error.

Mariannemariano answered 7/7, 2017 at 11:47 Comment(9)
why impossible? simply provide your custom specialization of OPs templatesSociolinguistics
By "user-defined replacement" I mean global replacement that completely hides build-in implementation, not some specialization.Mariannemariano
It would be easy enough to make std::new use a global function called memory_allocator, and have the runtime library provide a default implementation of that.Uncritical
@MartinBonner Then again it won't be possible to globally replace this memory_allocator without facing "more than one instance of overloaded function matches arguments" error. Basically current "replaceable default implementation" is just a workaround for such error.Mariannemariano
@VTT. There is no overloading here. The putative memory_allocator function would take a size_t and return void*. Linkers have always been able to cope with using a function defined in the user code, or using the standard library function if the user code doesn't define such a function.Uncritical
@MartinBonner I guess you are talking about making this memory_allocator function a weak symbol to allow such behavior, right?Mariannemariano
That's one way of implementing it.Uncritical
@MartinBonner Weak symbols are out of question because they are not even part of C++. Could you specify some other way of achieving such behavior staying strictly inside of C++ standard boundaries? I don't know any.Mariannemariano
Let us continue this discussion in chat.Uncritical

© 2022 - 2024 — McMap. All rights reserved.