2
votes

§3.7.4.2/2 contains the following sentences:

The global operator delete with exactly two parameters, the second of which has type std::size_t, is a usual deallocation function. Similarly, the global operator delete[] with exactly one parameter is a usual deallocation function. The global operator delete[] with exactly two parameters, the second of which has type std::size_t, is a usual deallocation function.37

37) This deallocation function precludes use of an allocation function void operator new(std::size_t, std::size_t) as a placement allocation function.

Not only did I not understand the reason for this footnote, but also I noticed that this placement form, alluded in the footnote, doesn't exist in §18.6.1.3 Placement Forms [new.delete.placement].

Edit In order to verify what @Sander De Dicker is saying in his answer, I tested the following snippet:

#include <iostream>

void* operator new  (std::size_t count, int i1, int i2, int i3){
    void *p = malloc(count);
    if (!p) throw std::bad_alloc{};
    std::cout << "operator new" << '\n';
    return p;
}

void operator delete (void* p, int j1, int j2, int j3)
{
    free(p);
    std::cout << "operator delete" << '\n';
}

class A {
public:
    A() { std::cout << "A()" << '\n'; };
    ~A() { std::cout << "~A()" << '\n'; }
};

int main()
{
    try
    {
        A *p = new(0, 0, 0) A;
        delete p;
    }
    catch (std::bad_alloc&) { exit(1); }
}

In all 3 compilers that I have available (VS2015, clang and g++) the code invoked the placement operator new(size_t, int, int, int), but didn't invoke the placement operator delete(void*, int, int, int), but the operator delete(void*). Now, I'm more confused than I was when I posted the question.

Live example

1
I think it means that one (a user) cannot define an operator new(size_t, size_t) because this requires a corresponding operator delete(size_t, size_t) for deletions that occur due to exceptions being thrown during the construction within the allocation via this placement-new function. - dyp
@dyp But according to §18.6.1.3, the language does not allow this placement form for operator new. - Belloc
I don't quite see how [new.delete.placement] forbids this allocation function. Could you please elaborate? Note: I've used the term "requires" in my previous comment, but that is too strong: A corresponding deallocation function is not required, but w/o a corresponding deallocation function, you'd get a leak in that scenario. (I'm not sure if the library version would get called.) - dyp
Placement delete is an odd beast; it is only called when the constructor called by the placement new throws. - T.C.
That paragraph only deals with handling deallocation when the construction throws. delete x; is specified separately (in 5.3.5 [expr.delete]) and only uses the normal deallocation functions. Practical reason: the compiler only knows what arguments to pass to the placement delete while you are in the placement new. - T.C.

1 Answers

3
votes

C.3.2 [diff.cpp11.basic] explains that :

C.3.2 Clause 3 : basic concepts [diff.cpp11.basic]

3.7.4.2

Change: New usual (non-placement) deallocator

Rationale: Required for sized deallocation.

Effect on original feature: Valid C++ 2011 code could declare a global placement allocation function and deallocation function as follows:

void operator new(std::size_t, std::size_t);

void operator delete(void*, std::size_t) noexcept;

In this International Standard, however, the declaration of operator delete might match a predefined usual (non-placement) operator delete (3.7.4). If so, the program is ill-formed, as it was for class member allocation functions and deallocation functions (5.3.4).

In other words, this is a breaking change with the previous standard (C++11). In C++11, it was allowed to define such operator delete, whereas in C++14 the program would be ill formed.

Edit - some further clarification based on comments :

  • 18.6.1.3 [new.delete.placement] lists the reserved placement forms. You are however allowed to declare other (non-reserved) ones in your code. So, that part of the standard doesn't apply to your question.

  • 3.7.4.2 [basic.stc.dynamic.deallocation] has the above mentioned change compared to C++11, which disallows the placement forms operator new(std::size_t, std::size_t) and operator delete(void*, std::size_t), because the latter might match a predefined non-placement operator delete. This change is further documented in C.3.2 [diff.cpp11.basic].

  • Your edited question has already been addressed in comments (1, 2) by @T.C. , but I'll include it here for completeness : The reason your custom operator delete(void*, int, int, int) is not called, is because the operator new(std::size_t, int, int, int) didn't throw an exception. 5.3.4 [expr.new] explains that in §20-23.