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 */
} 
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 7
    Because the non-placement `new` and `delete` are in the language since the beginning and the placement versions were added later? – axiac Jul 07 '17 at 11:44
  • @axiac 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. – Thomas Russell Jul 07 '17 at 11:45
  • 2
    @ThomasRussell the OP's question is why the non-placement `new` is not provided as a function implemented using *placement `new`*. – axiac Jul 07 '17 at 11:46
  • 1
    @axiac I think that is equally worthy of an answer as Curious's was. – underscore_d Jul 07 '17 at 11:53
  • 1
    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. – Vittorio Romeo Jul 07 '17 at 11:57
  • 2
    @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. – Curious Jul 07 '17 at 12:08
  • How does `library_delete` find the correct `operator delete` to return the memory to? – David Rodríguez - dribeas Jul 07 '17 at 12:31
  • 1
    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 manually – Curious Jul 07 '17 at 12:40
  • 1
    @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. – Vittorio Romeo Jul 07 '17 at 12:42
  • @VittorioRomeo I don't believe there is any magic, your library replacements work just fine, and I would use them myself. – Curious Jul 07 '17 at 12:43
  • @DavidRodríguez-dribeas seems to disagree. – Vittorio Romeo Jul 07 '17 at 12:43
  • @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. – Curious Jul 07 '17 at 12:45
  • 2
    I marked this as a duplicate because it has exactly the same answer as that other question: https://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. – Cody Gray - on strike Jul 07 '17 at 13:02
  • @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. – Vittorio Romeo Jul 07 '17 at 13:15
  • 1
    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. – Cody Gray - on strike Jul 07 '17 at 13:21
  • 1
    @CodyGray: I see. I found [this article](http://eli.thegreenplace.net/2015/c-deleting-destructors-and-virtual-operator-delete/) 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`. – Vittorio Romeo Jul 07 '17 at 13:31
  • 1
    @VittorioRomeo I find your link to be a more convincing answer than any of the current ones, care to self answer? – Passer By Jul 07 '17 at 14:01
  • @CodyGray questions can be different and have the same answers. – Kat Jul 07 '17 at 16:27
  • @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. – Curious Jul 07 '17 at 21:36
  • 1
    @Kat 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. – Cody Gray - on strike Jul 08 '17 at 01:14
  • 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. – T.C. Jul 08 '17 at 01:52
  • @T.C. Mind if I use the information you gave in my answer? – Curious Jul 08 '17 at 03:02
  • 1
    @Curious Feel free. – T.C. Jul 08 '17 at 03:03
  • 1
    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. – Damon Jul 08 '17 at 07:58

3 Answers3

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).

Curious
  • 20,870
  • 8
  • 61
  • 146
  • 8
    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." – Martin Bonner supports Monica Jul 07 '17 at 11:55
  • 3
    @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" – Curious Jul 07 '17 at 11:58
  • Variable templates... In order to return correct type (instead of `void*`) you'd need plain ol' templates, which we hadn't back then. – el.pescado - нет войне Jul 07 '17 at 21:03
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.

axiac
  • 68,258
  • 9
  • 99
  • 134
  • 3
    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. – Agent_L Jul 07 '17 at 12:27
  • 3
    Placement new is in Cfront 2.0, released in 1989. – T.C. Jul 08 '17 at 01:52
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.

user7860670
  • 35,849
  • 4
  • 58
  • 84
  • 2
    why impossible? simply provide your custom specialization of OPs templates – 463035818_is_not_an_ai Jul 07 '17 at 11:48
  • 1
    By "user-defined replacement" I mean *global* replacement that completely hides build-in implementation, not some specialization. – user7860670 Jul 07 '17 at 11:49
  • 1
    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. – Martin Bonner supports Monica Jul 07 '17 at 11:57
  • 2
    @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. – user7860670 Jul 07 '17 at 12:00
  • @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. – Martin Bonner supports Monica Jul 07 '17 at 12:02
  • 2
    @MartinBonner I guess you are talking about making this `memory_allocator` function a weak symbol to allow such behavior, right? – user7860670 Jul 07 '17 at 12:09
  • That's one way of implementing it. – Martin Bonner supports Monica Jul 07 '17 at 12:11
  • 2
    @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. – user7860670 Jul 07 '17 at 12:15
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/148603/discussion-between-martin-bonner-and-vtt). – Martin Bonner supports Monica Jul 07 '17 at 12:16